[CalendarServer-changes] [11293] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Tue Jun 4 13:52:00 PDT 2013


Revision: 11293
          http://trac.calendarserver.org//changeset/11293
Author:   cdaboo at apple.com
Date:     2013-06-04 13:52:00 -0700 (Tue, 04 Jun 2013)
Log Message:
-----------
Make sure wiki access failure does not generate a 500. Actually make that generate a 503 inside a multi-status
response for the failed child resource.

Modified Paths:
--------------
    CalendarServer/trunk/twext/web2/dav/resource.py
    CalendarServer/trunk/twistedcaldav/directory/wiki.py
    CalendarServer/trunk/twistedcaldav/method/propfind.py
    CalendarServer/trunk/twistedcaldav/method/report_addressbook_query.py
    CalendarServer/trunk/twistedcaldav/method/report_calendar_query.py
    CalendarServer/trunk/twistedcaldav/method/report_multiget_common.py
    CalendarServer/trunk/twistedcaldav/method/report_sync_collection.py
    CalendarServer/trunk/twistedcaldav/resource.py
    CalendarServer/trunk/twistedcaldav/storebridge.py

Modified: CalendarServer/trunk/twext/web2/dav/resource.py
===================================================================
--- CalendarServer/trunk/twext/web2/dav/resource.py	2013-06-04 19:31:04 UTC (rev 11292)
+++ CalendarServer/trunk/twext/web2/dav/resource.py	2013-06-04 20:52:00 UTC (rev 11293)
@@ -8,10 +8,10 @@
 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 # copies of the Software, and to permit persons to whom the Software is
 # furnished to do so, subject to the following conditions:
-# 
+#
 # The above copyright notice and this permission notice shall be included in all
 # copies or substantial portions of the Software.
-# 
+#
 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -137,32 +137,33 @@
     # use them.  This (generic) class does not.
 
     def liveProperties(self):
-    
+
         return (
-            (dav_namespace, "resourcetype"              ),
-            (dav_namespace, "getetag"                   ),
-            (dav_namespace, "getcontenttype"            ),
-            (dav_namespace, "getcontentlength"          ),
-            (dav_namespace, "getlastmodified"           ),
-            (dav_namespace, "creationdate"              ),
-            (dav_namespace, "displayname"               ),
-            (dav_namespace, "supportedlock"             ),
-            (dav_namespace, "supported-report-set"      ), # RFC 3253, section 3.1.5
+            (dav_namespace, "resourcetype"),
+            (dav_namespace, "getetag"),
+            (dav_namespace, "getcontenttype"),
+            (dav_namespace, "getcontentlength"),
+            (dav_namespace, "getlastmodified"),
+            (dav_namespace, "creationdate"),
+            (dav_namespace, "displayname"),
+            (dav_namespace, "supportedlock"),
+            (dav_namespace, "supported-report-set"), # RFC 3253, section 3.1.5
            #(dav_namespace, "owner"                     ), # RFC 3744, section 5.1
            #(dav_namespace, "group"                     ), # RFC 3744, section 5.2
-            (dav_namespace, "supported-privilege-set"   ), # RFC 3744, section 5.3
+            (dav_namespace, "supported-privilege-set"), # RFC 3744, section 5.3
             (dav_namespace, "current-user-privilege-set"), # RFC 3744, section 5.4
-            (dav_namespace, "current-user-principal"    ), # RFC 5397, Section 3
-            (dav_namespace, "acl"                       ), # RFC 3744, section 5.5
-            (dav_namespace, "acl-restrictions"          ), # RFC 3744, section 5.6
-            (dav_namespace, "inherited-acl-set"         ), # RFC 3744, section 5.7
-            (dav_namespace, "principal-collection-set"  ), # RFC 3744, section 5.8
-            (dav_namespace, "quota-available-bytes"     ), # RFC 4331, section 3
-            (dav_namespace, "quota-used-bytes"          ), # RFC 4331, section 4
-    
+            (dav_namespace, "current-user-principal"), # RFC 5397, Section 3
+            (dav_namespace, "acl"), # RFC 3744, section 5.5
+            (dav_namespace, "acl-restrictions"), # RFC 3744, section 5.6
+            (dav_namespace, "inherited-acl-set"), # RFC 3744, section 5.7
+            (dav_namespace, "principal-collection-set"), # RFC 3744, section 5.8
+            (dav_namespace, "quota-available-bytes"), # RFC 4331, section 3
+            (dav_namespace, "quota-used-bytes"), # RFC 4331, section 4
+
             (twisted_dav_namespace, "resource-class"),
         )
 
+
     def deadProperties(self):
         """
         Provides internal access to the WebDAV dead property store.
@@ -186,6 +187,7 @@
             self._dead_properties = NonePropertyStore(self)
         return self._dead_properties
 
+
     def hasProperty(self, property, request):
         """
         See L{IDAVResource.hasProperty}.
@@ -205,7 +207,7 @@
                 d = self.hasQuota(request)
                 d.addCallback(lambda result: result)
                 return d
-        
+
         return succeed(
             qname in self.liveProperties() or
             self.deadProperties().contains(qname)
@@ -251,8 +253,8 @@
                 if name == "getcontentlength":
                     length = self.contentLength()
                     if length is None:
-                        # TODO: really we should "render" the resource and 
-                        # determine its size from that but for now we just 
+                        # TODO: really we should "render" the resource and
+                        # determine its size from that but for now we just
                         # return an empty element.
                         returnValue(element.GETContentLength(""))
                     else:
@@ -412,6 +414,7 @@
 
         return maybeDeferred(defer)
 
+
     def removeProperty(self, property, request):
         """
         See L{IDAVResource.removeProperty}.
@@ -441,6 +444,7 @@
 
         return maybeDeferred(defer)
 
+
     @inlineCallbacks
     def listProperties(self, request):
         """
@@ -451,7 +455,7 @@
         # Add dynamic live properties that exist
         dynamicLiveProperties = (
             (dav_namespace, "quota-available-bytes"),
-            (dav_namespace, "quota-used-bytes"     ),
+            (dav_namespace, "quota-used-bytes"),
         )
         for dqname in dynamicLiveProperties:
             has = (yield self.hasProperty(dqname, request))
@@ -467,6 +471,7 @@
 
         returnValue(qnames)
 
+
     def listAllprop(self, request):
         """
         Some DAV properties should not be returned to a C{DAV:allprop}
@@ -496,6 +501,7 @@
         d.addCallback(doList)
         return d
 
+
     def hasDeadProperty(self, property):
         """
         Same as L{hasProperty}, but bypasses the live property store
@@ -508,6 +514,7 @@
 
         return self.deadProperties().contains(qname)
 
+
     def readDeadProperty(self, property):
         """
         Same as L{readProperty}, but bypasses the live property store
@@ -520,6 +527,7 @@
 
         return self.deadProperties().get(qname)
 
+
     def writeDeadProperty(self, property):
         """
         Same as L{writeProperty}, but bypasses the live property store
@@ -533,6 +541,7 @@
         """
         self.deadProperties().set(property)
 
+
     def removeDeadProperty(self, property):
         """
         Same as L{removeProperty}, but bypasses the live property
@@ -546,6 +555,7 @@
 
             self.deadProperties().delete(qname)
 
+
     #
     # Overrides some methods in MetaDataMixin in order to allow DAV properties
     # to override the values of some HTTP metadata.
@@ -558,6 +568,7 @@
         else:
             return super(DAVPropertyMixIn, self).contentType()
 
+
     def displayName(self):
         if self.hasDeadProperty((element.dav_namespace, "displayname")):
             return str(self.readDeadProperty(
@@ -566,6 +577,8 @@
         else:
             return super(DAVPropertyMixIn, self).displayName()
 
+
+
 class DAVResource (DAVPropertyMixIn, StaticRenderMixin):
     """
     WebDAV resource.
@@ -584,6 +597,7 @@
                 for principalCollection in principalCollections
             ])
 
+
     ##
     # DAV
     ##
@@ -597,6 +611,7 @@
         """
         unimplemented(self)
 
+
     def isCollection(self):
         """
         See L{IDAVResource.isCollection}.
@@ -606,6 +621,7 @@
         """
         unimplemented(self)
 
+
     def findChildren(
         self, depth, request, callback,
         privileges=None, inherited_aces=None
@@ -636,7 +652,7 @@
 
             if privileges is None:
                 return child
-   
+
             d = child.checkPrivileges(
                 request, privileges,
                 inherited_aces=inherited_aces
@@ -681,9 +697,10 @@
 
         return completionDeferred
 
+
     @inlineCallbacks
     def findChildrenFaster(
-        self, depth, request, okcallback, badcallback, missingcallback,
+        self, depth, request, okcallback, badcallback, missingcallback, unavailablecallback,
         names, privileges, inherited_aces
     ):
         """
@@ -692,7 +709,7 @@
         This implementation works for C{depth} values of C{"0"},
         C{"1"}, and C{"infinity"}.  As long as C{self.listChildren} is
         implemented
-        
+
         @param depth: a C{str} for the depth: "0", "1" and "infinity"
         only allowed.
         @param request: the L{Request} for the current request in
@@ -717,7 +734,7 @@
             returnValue(None)
 
         # First find all depth 1 children
-        names1= []
+        names1 = []
         namesDeep = []
         collections1 = []
         if names:
@@ -732,7 +749,13 @@
         childnames = list((yield self.listChildren()))
         for childname in childnames:
             childpath = joinURL(basepath, urllib.quote(childname))
-            child = (yield request.locateChildResource(self, childname))
+            try:
+                child = (yield request.locateChildResource(self, childname))
+            except HTTPError, e:
+                log.error("Resource cannot be located: %s" % (str(e),))
+                if unavailablecallback:
+                    unavailablecallback(childpath)
+                continue
             if child is not None:
                 if child.isCollection():
                     collections1.append((child, childpath + "/"))
@@ -788,18 +811,19 @@
                         yield collection.inheritedACEsforChildren(request)
                     )
                     yield collection.findChildrenFaster(
-                        depth, request, okcallback, badcallback, missingcallback,
+                        depth, request, okcallback, badcallback, missingcallback, unavailablecallback,
                         child_collections[collection_name] if names else None, privileges,
                         inherited_aces=collection_inherited_aces
                     )
-                
+
         returnValue(None)
 
+
     @inlineCallbacks
     def checkACLPrivilege(
         self, request, acl, privyset, privileges, inherited_aces
     ):
-        
+
         if acl is None:
             returnValue(False)
 
@@ -840,6 +864,7 @@
 
         returnValue(len(denied) + len(pending) == 0)
 
+
     def fullAccessControlList(self, acl, inherited_aces):
         """
         See L{IDAVResource.accessControlList}.
@@ -876,7 +901,8 @@
         acl = element.ACL(*aces)
 
         return acl
-    
+
+
     def supportedReports(self):
         """
         See L{IDAVResource.supportedReports}.
@@ -892,6 +918,7 @@
         result.append(element.Report(customxml.CalendarServerPrincipalSearch(),))
         return result
 
+
     ##
     # Authentication
     ##
@@ -931,6 +958,7 @@
         d.addCallback(whenAuthenticated)
         return d
 
+
     def authenticate(self, request):
         """
         Authenticate the given request against the portal, setting
@@ -1046,6 +1074,7 @@
             request.authzUser = element.Principal(element.Unauthenticated())
             return succeed((request.authnUser, request.authzUser))
 
+
     ##
     # ACL
     ##
@@ -1061,6 +1090,7 @@
         else:
             return unauthenticatedPrincipal
 
+
     def principalCollections(self):
         """
         See L{IDAVResource.principalCollections}.
@@ -1070,6 +1100,7 @@
         else:
             return ()
 
+
     def defaultRootAccessControlList(self):
         """
         @return: the L{element.ACL} element containing the default
@@ -1082,6 +1113,7 @@
         #
         return readonlyACL
 
+
     def defaultAccessControlList(self):
         """
         @return: the L{element.ACL} element containing the default
@@ -1093,6 +1125,7 @@
         #
         return element.ACL()
 
+
     def setAccessControlList(self, acl):
         """
         See L{IDAVResource.setAccessControlList}.
@@ -1121,7 +1154,7 @@
         This implementation stores the ACL in the private property
         """
         # C{(L{twisted_private_namespace}, "acl")}.
-        
+
         # Steps for ACL evaluation:
         #  1. Check that ace's on incoming do not match a protected ace
         #  2. Check that ace's on incoming do not match an inherited ace
@@ -1333,7 +1366,7 @@
         if recurse:
             yield self.findChildren(
                 "infinity", request,
-                lambda x, y: resources.append((x,y))
+                lambda x, y: resources.append((x, y))
             )
 
         for resource, uri in resources:
@@ -1378,14 +1411,15 @@
 
             denied += pending # If no matching ACE, then denied
 
-            if denied: 
+            if denied:
                 errors.append((uri, denied))
 
         if errors:
             raise AccessDeniedError(errors,)
-        
+
         returnValue(None)
 
+
     def supportedPrivileges(self, request):
         """
         See L{IDAVResource.supportedPrivileges}.
@@ -1395,6 +1429,7 @@
         """
         return succeed(davPrivilegeSet)
 
+
     def currentPrivileges(self, request):
         """
         See L{IDAVResource.currentPrivileges}.
@@ -1405,6 +1440,7 @@
         current = self.currentPrincipal(request)
         return self.privilegesForPrincipal(current, request)
 
+
     @inlineCallbacks
     def accessControlList(
         self, request, inheritance=True,
@@ -1474,20 +1510,20 @@
             if inherited_aces is None:
                 if myURL != "/":
                     parentURL = parentForURL(myURL)
-    
+
                     parent = (yield request.locateResource(parentURL))
-    
+
                     if parent:
                         parent_acl = (yield
                             parent.accessControlList(
                                 request, inheritance=True, expanding=True
                             )
                         )
-    
+
                         # Check disabled
                         if parent_acl is None:
                             returnValue(None)
-    
+
                         for ace in parent_acl.children:
                             if ace.inherited:
                                 aces.append(ace)
@@ -1518,6 +1554,7 @@
 
         returnValue(acl)
 
+
     def inheritedACEsforChildren(self, request):
         """
         Do some optimisation of access control calculation by
@@ -1528,7 +1565,7 @@
         @return: a C{list} of L{Ace}s that child resources of this one
             will inherit.
         """
-        
+
         # Get the parent ACLs with inheritance and preserve the
         # <inheritable> element.
 
@@ -1559,6 +1596,7 @@
         d.addCallback(gotACL)
         return d
 
+
     def inheritedACLSet(self):
         """
         @return: a sequence of L{element.HRef}s from which ACLs are
@@ -1568,6 +1606,7 @@
         """
         return []
 
+
     def principalsForAuthID(self, request, authid):
         """
         Return authentication and authorization principal identifiers
@@ -1596,6 +1635,7 @@
         d.addCallback(lambda authzPrincipal: (authnPrincipal, authzPrincipal))
         return d
 
+
     def findPrincipalForAuthID(self, authid):
         """
         Return authentication and authorization principal identifiers
@@ -1616,12 +1656,13 @@
                 return principal
         return None
 
+
     def authorizationPrincipal(self, request, authid, authnPrincipal):
         """
         Determine the authorization principal for the given request
         and authentication principal.  This implementation simply uses
         that authentication principal as the authorization principal.
-        
+
         @param request: the L{IRequest} for the request in progress.
         @param authid: a string containing the
             authentication/authorization identifier for the principal
@@ -1633,7 +1674,8 @@
             and URI respectively.
         """
         return succeed(authnPrincipal)
-        
+
+
     def samePrincipal(self, principal1, principal2):
         """
         Check whether the two principals are exactly the same in terms of
@@ -1663,7 +1705,8 @@
                 return True
         else:
             return False
-                
+
+
     def matchPrincipal(self, principal1, principal2, request):
         """
         Check whether the principal1 is a principal in the set defined
@@ -1739,6 +1782,7 @@
         d.addCallback(cache)
         return d
 
+
     @inlineCallbacks
     def principalIsGroupMember(self, principal1, principal2, request):
         """
@@ -1759,7 +1803,7 @@
             returnValue(isContained)
         returnValue(False)
 
-        
+
     def validPrincipal(self, ace_principal, request):
         """
         Check whether the supplied principal is valid for this resource.
@@ -1793,6 +1837,7 @@
 
         return maybeDeferred(defer)
 
+
     def validHrefPrincipal(self, href_principal, request):
         """
         Check whether the supplied principal (in the form of an Href)
@@ -1809,7 +1854,7 @@
 
         # Must have the principal resource type and must match the
         # principal-URL
-        
+
         def _matchPrincipalURL(resource):
             return (
                 isPrincipalResource(resource) and
@@ -1820,6 +1865,7 @@
         d.addCallback(_matchPrincipalURL)
         return d
 
+
     def resolvePrincipal(self, principal, request):
         """
         Resolves a L{element.Principal} element into a L{element.HRef}
@@ -1911,6 +1957,7 @@
 
         return succeed(None)
 
+
     @inlineCallbacks
     def privilegesForPrincipal(self, principal, request):
         """
@@ -1954,6 +2001,7 @@
 
         returnValue(allowed)
 
+
     def matchACEinACL(self, acl, ace):
         """
         Find an ACE in the ACL that matches the supplied ACE's principal.
@@ -1964,15 +2012,16 @@
         for a in acl.children:
             if self.samePrincipal(a.principal, ace.principal):
                 return a
-        
+
         return None
-    
+
+
     def principalSearchPropertySet(self):
         """
         @return: a L{element.PrincipalSearchPropertySet} element describing the
         principal properties that can be searched on this principal collection,
         or C{None} if this is not a principal collection.
-        
+
         This implementation returns None. Principal collection resources must
         override and return their own suitable response.
         """
@@ -1981,18 +2030,18 @@
     ##
     # Quota
     ##
-    
+
     """
     The basic policy here is to define a private 'quota-root' property
     on a collection.  That property will contain the maximum allowed
     bytes for the collections and all its contents.
-    
+
     In order to determine the quota property values on a resource, the
     server must look for the private property on that resource and any
     of its parents. If found on a parent, then that parent should be
     queried for quota information. If not found, no quota exists for
     the resource.
-    
+
     To determine that actual quota in use we will cache the used byte
     count on the quota-root collection in another private property. It
     is the servers responsibility to keep that property up to date by
@@ -2013,10 +2062,10 @@
             quota-used-bytes, or C{None} if quota is not defined on
             the resource.
         """
-        
+
         # See if already cached
         if hasattr(request, "quota"):
-            if request.quota.has_key(self):
+            if self in request.quota:
                 return succeed(request.quota[self])
         else:
             request.quota = {}
@@ -2032,7 +2081,7 @@
                             available = 0
                         request.quota[self] = (available, used)
                         return (available, used)
-    
+
                     d = qroot_resource.currentQuotaUse(request)
                     d.addCallback(gotUsage)
                     return d
@@ -2040,33 +2089,35 @@
             request.quota[self] = None
             return None
 
-            
         d = self.quotaRootResource(request)
         d.addCallback(gotQuotaRootResource)
         return d
-    
+
+
     def hasQuota(self, request):
         """
         Check whether this resource is under quota control by checking
         each parent to see if it has a quota root.
-        
+
         @return: C{True} if under quota control, C{False} if not.
         """
 
         def gotQuotaRootResource(qroot_resource):
-            
+
             return qroot_resource is not None
-        
+
         d = self.quotaRootResource(request)
         d.addCallback(gotQuotaRootResource)
         return d
 
+
     def hasQuotaRoot(self, request):
         """
         @return: a C{True} if this resource has quota root, C{False} otherwise.
         """
         return self.hasDeadProperty(TwistedQuotaRootProperty)
-    
+
+
     def quotaRoot(self, request):
         """
         @return: a C{int} containing the maximum allowed bytes if this
@@ -2077,12 +2128,13 @@
             return int(str(self.readDeadProperty(TwistedQuotaRootProperty)))
         else:
             return None
-    
+
+
     @inlineCallbacks
     def quotaRootResource(self, request):
         """
         Return the quota root for this resource.
-        
+
         @return: L{DAVResource} or C{None}
         """
 
@@ -2106,6 +2158,7 @@
 
         returnValue(None)
 
+
     def setQuotaRoot(self, request, maxsize):
         """
         @param maxsize: a C{int} containing the maximum allowed bytes
@@ -2116,14 +2169,15 @@
         assert maxsize is None or isinstance(maxsize, int), (
             "maxsize must be an int or None"
         )
-        
+
         if maxsize is not None:
             self.writeDeadProperty(TwistedQuotaRootProperty(str(maxsize)))
         else:
             # Remove both the root and the cached used value
             self.removeDeadProperty(TwistedQuotaRootProperty)
             self.removeDeadProperty(TwistedQuotaUsedProperty)
-    
+
+
     def quotaSize(self, request):
         """
         Get the size of this resource (if its a collection get total
@@ -2134,19 +2188,20 @@
         """
         unimplemented(self)
 
+
     def checkQuota(self, request, available):
         """
         Check to see whether all quota roots have sufficient available
         bytes.  We currently do not use hierarchical quota checks -
         i.e. only the most immediate quota root parent is checked for
         quota.
-        
+
         @param available: a C{int} containing the additional quota
             required.
         @return: C{True} if there is sufficient quota remaining on all
             quota roots, C{False} otherwise.
         """
-        
+
         def _defer(quotaroot):
             if quotaroot:
                 # Check quota on this root (if it has one)
@@ -2154,13 +2209,14 @@
                 if quota is not None:
                     if available > quota[0]:
                         return False
-    
+
             return True
 
         d = self.quotaRootResource(request)
         d.addCallback(_defer)
         return d
 
+
     def quotaSizeAdjust(self, request, adjust):
         """
         Update the quota used value on all quota root parents of this
@@ -2170,8 +2226,7 @@
             (positive) or removed (negative) that should be used to
             adjust the cached total.
         """
-        
-        
+
         def _defer(quotaroot):
             if quotaroot:
                 # Check quota on this root (if it has one)
@@ -2181,6 +2236,7 @@
         d.addCallback(_defer)
         return d
 
+
     def currentQuotaUse(self, request):
         """
         Get the cached quota use value, or if not present (or invalid)
@@ -2194,7 +2250,7 @@
         assert self.hasQuotaRoot(request), (
             "Quota use only on quota root collection"
         )
-        
+
         # Try to get the cached value property
         if self.hasDeadProperty(TwistedQuotaUsedProperty):
             return succeed(
@@ -2210,6 +2266,7 @@
             d.addCallback(_defer)
             return d
 
+
     def updateQuotaUse(self, request, adjust):
         """
         Update the quota used value on this resource.
@@ -2222,11 +2279,11 @@
             or C{None} if not quota controlled.
         """
         assert self.isCollection(), "Only collections can have a quota root"
-        
+
         # Get current value
         def _defer(size):
             size += adjust
-            
+
             # Sanity check the resulting size
             if size >= 0:
                 self.writeDeadProperty(TwistedQuotaUsedProperty(str(size)))
@@ -2242,7 +2299,8 @@
         d = self.currentQuotaUse(request)
         d.addCallback(_defer)
         return d
-        
+
+
     ##
     # HTTP
     ##
@@ -2260,7 +2318,7 @@
                 request.unparseURL(
                     path=urllib.quote(
                         urllib.unquote(request.path),
-                        safe=':/')+'/'
+                        safe=':/') + '/'
                 )
             )
 
@@ -2291,6 +2349,8 @@
         d = maybeDeferred(super(DAVResource, self).renderHTTP, request)
         return d.addCallbacks(setHeaders, onError)
 
+
+
 class DAVLeafResource (DAVResource, LeafResource):
     """
     DAV resource with no children.
@@ -2301,6 +2361,8 @@
     ):
         return succeed(None)
 
+
+
 class DAVPrincipalResource (DAVResource):
     """
     Resource representing a WebDAV principal.  (RFC 3744, section 2)
@@ -2312,20 +2374,23 @@
     ##
 
     def liveProperties(self):
-        
+
         return super(DAVPrincipalResource, self).liveProperties() + (
             (dav_namespace, "alternate-URI-set"),
-            (dav_namespace, "principal-URL"    ),
-            (dav_namespace, "group-member-set" ),
-            (dav_namespace, "group-membership" ),
+            (dav_namespace, "principal-URL"),
+            (dav_namespace, "group-member-set"),
+            (dav_namespace, "group-membership"),
         )
 
+
     def davComplianceClasses(self):
         return ("1", "access-control",)
 
+
     def isCollection(self):
         return False
 
+
     def readProperty(self, property, request):
         def defer():
             if type(property) is tuple:
@@ -2352,7 +2417,7 @@
                             element.HRef(p.principalURL())
                             for p in members
                         ])
-                    
+
                     d = self.groupMembers()
                     d.addCallback(callback)
                     return d
@@ -2363,7 +2428,7 @@
                             element.HRef(g.principalURL())
                             for g in memberships
                         ])
-                    
+
                     d = self.groupMemberships()
                     d.addCallback(callback)
                     return d
@@ -2383,6 +2448,7 @@
 
         return maybeDeferred(defer)
 
+
     ##
     # ACL
     ##
@@ -2397,6 +2463,7 @@
         """
         return ()
 
+
     def principalURL(self):
         """
         See L{IDAVPrincipalResource.principalURL}.
@@ -2407,6 +2474,7 @@
         """
         unimplemented(self)
 
+
     def groupMembers(self):
         """
         This implementation returns a Deferred which fires with C{()},
@@ -2418,6 +2486,7 @@
         """
         return succeed(())
 
+
     def expandedGroupMembers(self):
         """
         This implementation returns a Deferred which fires with C{()},
@@ -2440,6 +2509,7 @@
         """
         unimplemented(self)
 
+
     def principalMatch(self, href):
         """
         Check whether the supplied principal matches this principal or
@@ -2458,6 +2528,7 @@
             )
             return d
 
+
     @inlineCallbacks
     def containsPrincipal(self, principal):
         """
@@ -2472,6 +2543,7 @@
         returnValue(principal in members)
 
 
+
 class DAVPrincipalCollectionResource (DAVResource):
     """
     WebDAV principal collection resource.  (RFC 3744, section 5.8)
@@ -2512,9 +2584,11 @@
         )
 
 
+
 class AccessDeniedError(Exception):
+
     def __init__(self, errors):
-        """ 
+        """
         An error to be raised when some request fails to meet
         sufficient access privileges for a resource.
 
@@ -2529,6 +2603,8 @@
                            % (errors,))
         self.errors = errors
 
+
+
 ##
 # Utilities
 ##
@@ -2541,6 +2617,8 @@
     else:
         return True
 
+
+
 class TwistedACLInheritable (WebDAVEmptyElement):
     """
     When set on an ACE, this indicates that the ACE privileges should

Modified: CalendarServer/trunk/twistedcaldav/directory/wiki.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/wiki.py	2013-06-04 19:31:04 UTC (rev 11292)
+++ CalendarServer/trunk/twistedcaldav/directory/wiki.py	2013-06-04 20:52:00 UTC (rev 11293)
@@ -23,23 +23,25 @@
     "WikiDirectoryService",
 ]
 
-from twisted.internet.defer import inlineCallbacks, returnValue
-from txdav.xml import element as davxml
-from twisted.web.xmlrpc import Proxy, Fault
-from twext.web2.http import HTTPError, StatusResponse
-from twext.web2.auth.wrapper import UnauthorizedResponse
-from twext.web2 import responsecode
+from calendarserver.platform.darwin.wiki import accessForUserToWiki
 
+from twext.internet.gaiendpoint import MultiFailure
 from twext.python.log import Logger
+from twext.web2 import responsecode
+from twext.web2.auth.wrapper import UnauthorizedResponse
+from twext.web2.dav.resource import TwistedACLInheritable
+from twext.web2.http import HTTPError, StatusResponse
 
-from twext.web2.dav.resource import TwistedACLInheritable
-from twistedcaldav.config import config
-from twistedcaldav.directory.directory import (DirectoryService,
-                                               DirectoryRecord,
-                                               UnknownRecordTypeError)
-from calendarserver.platform.darwin.wiki import accessForUserToWiki
+from twisted.internet.defer import inlineCallbacks, returnValue
 from twisted.web.error import Error as WebError
+from twisted.web.xmlrpc import Proxy, Fault
 
+from twistedcaldav.config import config
+from twistedcaldav.directory.directory import DirectoryService, \
+    DirectoryRecord, UnknownRecordTypeError
+
+from txdav.xml import element as davxml
+
 log = Logger()
 
 class WikiDirectoryService(DirectoryService):
@@ -58,31 +60,36 @@
     def __repr__(self):
         return "<%s %r>" % (self.__class__.__name__, self.realmName)
 
+
     def __init__(self):
         super(WikiDirectoryService, self).__init__()
         self.byUID = {}
         self.byShortName = {}
 
+
     def recordTypes(self):
         return (WikiDirectoryService.recordType_wikis,)
 
+
     def listRecords(self, recordType):
         return ()
 
+
     def recordWithShortName(self, recordType, shortName):
         if recordType != WikiDirectoryService.recordType_wikis:
             raise UnknownRecordTypeError(recordType)
 
-        if self.byShortName.has_key(shortName):
+        if shortName in self.byShortName:
             record = self.byShortName[shortName]
             return record
 
         record = self._addRecord(shortName)
         return record
 
+
     def recordWithUID(self, uid):
 
-        if self.byUID.has_key(uid):
+        if uid in self.byUID:
             record = self.byUID[uid]
             return record
 
@@ -93,6 +100,7 @@
         else:
             return None
 
+
     def _addRecord(self, shortName):
 
         record = WikiDirectoryRecord(
@@ -106,6 +114,7 @@
         return record
 
 
+
 class WikiDirectoryRecord(DirectoryRecord):
     """
     L{DirectoryRecord} implementation for Wikis.
@@ -125,6 +134,7 @@
         self.enabled = True
 
 
+
 @inlineCallbacks
 def getWikiAccess(userID, wikiID, method=None):
     """
@@ -189,6 +199,12 @@
             (userID, wikiID, access))
         returnValue(access)
 
+    except MultiFailure, e:
+        log.error("Wiki ACL error: user [%s], wiki [%s], MultiFailure [%s]" %
+            (userID, wikiID, e))
+        raise HTTPError(StatusResponse(responsecode.SERVICE_UNAVAILABLE,
+            "\n".join([str(f) for f in e.failures])))
+
     except Fault, fault:
 
         log.debug("Wiki ACL result: user [%s], wiki [%s], FAULT [%s]" % (userID,
@@ -202,8 +218,9 @@
             raise HTTPError(StatusResponse(responsecode.NOT_FOUND,
                 fault.faultString))
 
-        else: # Unknown fault returned from wiki server.  Log the error and
-              # return 503 Service Unavailable to the client.
+        else:
+            # Unknown fault returned from wiki server.  Log the error and
+            # return 503 Service Unavailable to the client.
             log.error("Wiki ACL error: user [%s], wiki [%s], FAULT [%s]" %
                 (userID, wikiID, fault))
             raise HTTPError(StatusResponse(responsecode.SERVICE_UNAVAILABLE,
@@ -223,14 +240,16 @@
             raise HTTPError(StatusResponse(responsecode.NOT_FOUND,
                 "Unknown Wiki"))
 
-        else: # Unknown fault returned from wiki server.  Log the error and
-              # return 503 Service Unavailable to the client.
+        else:
+            # Unknown fault returned from wiki server.  Log the error and
+            # return 503 Service Unavailable to the client.
             log.error("Wiki ACL error: user [%s], wiki [%s], status [%s]" %
                 (userID, wikiID, status))
             raise HTTPError(StatusResponse(responsecode.SERVICE_UNAVAILABLE,
                 w.message))
 
 
+
 @inlineCallbacks
 def getWikiACL(resource, request):
     """
@@ -271,55 +290,55 @@
         # The ACL we returns has ACEs for the end-user and the wiki principal
         # in case authzUser is the wiki principal.
         if access == "read":
-            request.wikiACL =   davxml.ACL(
-                                    davxml.ACE(
-                                        request.authnUser,
-                                        davxml.Grant(
-                                            davxml.Privilege(davxml.Read()),
-                                            davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
-                                            
-                                            # We allow write-properties so that direct sharees can change
-                                            # e.g. calendar color properties
-                                            davxml.Privilege(davxml.WriteProperties()),
-                                        ),
-                                        TwistedACLInheritable(),
-                                    ),
-                                    davxml.ACE(
-                                        davxml.Principal(
-                                            davxml.HRef.fromString("/principals/wikis/%s/" % (wikiID,))
-                                        ),
-                                        davxml.Grant(
-                                            davxml.Privilege(davxml.Read()),
-                                            davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
-                                        ),
-                                        TwistedACLInheritable(),
-                                    )
-                                )
+            request.wikiACL = davxml.ACL(
+                davxml.ACE(
+                    request.authnUser,
+                    davxml.Grant(
+                        davxml.Privilege(davxml.Read()),
+                        davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
+
+                        # We allow write-properties so that direct sharees can change
+                        # e.g. calendar color properties
+                        davxml.Privilege(davxml.WriteProperties()),
+                    ),
+                    TwistedACLInheritable(),
+                ),
+                davxml.ACE(
+                    davxml.Principal(
+                        davxml.HRef.fromString("/principals/wikis/%s/" % (wikiID,))
+                    ),
+                    davxml.Grant(
+                        davxml.Privilege(davxml.Read()),
+                        davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
+                    ),
+                    TwistedACLInheritable(),
+                )
+            )
             returnValue(request.wikiACL)
 
         elif access in ("write", "admin"):
-            request.wikiACL =   davxml.ACL(
-                                    davxml.ACE(
-                                        request.authnUser,
-                                        davxml.Grant(
-                                            davxml.Privilege(davxml.Read()),
-                                            davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
-                                            davxml.Privilege(davxml.Write()),
-                                        ),
-                                        TwistedACLInheritable(),
-                                    ),
-                                    davxml.ACE(
-                                        davxml.Principal(
-                                            davxml.HRef.fromString("/principals/wikis/%s/" % (wikiID,))
-                                        ),
-                                        davxml.Grant(
-                                            davxml.Privilege(davxml.Read()),
-                                            davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
-                                            davxml.Privilege(davxml.Write()),
-                                        ),
-                                        TwistedACLInheritable(),
-                                    )
-                                )
+            request.wikiACL = davxml.ACL(
+                davxml.ACE(
+                    request.authnUser,
+                    davxml.Grant(
+                        davxml.Privilege(davxml.Read()),
+                        davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
+                        davxml.Privilege(davxml.Write()),
+                    ),
+                    TwistedACLInheritable(),
+                ),
+                davxml.ACE(
+                    davxml.Principal(
+                        davxml.HRef.fromString("/principals/wikis/%s/" % (wikiID,))
+                    ),
+                    davxml.Grant(
+                        davxml.Privilege(davxml.Read()),
+                        davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
+                        davxml.Privilege(davxml.Write()),
+                    ),
+                    TwistedACLInheritable(),
+                )
+            )
             returnValue(request.wikiACL)
 
         else: # "no-access":

Modified: CalendarServer/trunk/twistedcaldav/method/propfind.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/propfind.py	2013-06-04 19:31:04 UTC (rev 11292)
+++ CalendarServer/trunk/twistedcaldav/method/propfind.py	2013-06-04 20:52:00 UTC (rev 11293)
@@ -135,14 +135,15 @@
     if depth in ("1", "infinity") and noRoot:
         resources = []
     else:
-        resources = [(True, self, my_url)]
+        resources = [(responsecode.OK, self, my_url)]
 
     yield self.findChildrenFaster(
         depth,
         request,
-        lambda x, y: resources.append((True, x, y)),
-        lambda x, y: resources.append((False, x, y)),
+        lambda x, y: resources.append((responsecode.OK, x, y)),
+        lambda x, y: resources.append((responsecode.FORBIDDEN, x, y)),
         None,
+        lambda x: resources.append((responsecode.SERVICE_UNAVAILABLE, None, x)),
         None,
         (davxml.Read(),),
         inherited_aces=filtered_aces,
@@ -152,8 +153,8 @@
     if depth == "1":
         request.childCacheURIs = []
 
-    for readable, resource, uri in resources:
-        if readable:
+    for respcode, resource, uri in resources:
+        if respcode == responsecode.OK:
             if search_properties is "names":
                 try:
                     resource_properties = (yield resource.listProperties(request))
@@ -225,7 +226,7 @@
                     elif hasattr(resource, "url"):
                         request.childCacheURIs.append(resource.url())
         else:
-            xml_response = davxml.StatusResponse(davxml.HRef(uri), davxml.Status.fromResponseCode(responsecode.FORBIDDEN))
+            xml_response = davxml.StatusResponse(davxml.HRef(uri), davxml.Status.fromResponseCode(respcode))
 
         xml_responses.append(xml_response)
 

Modified: CalendarServer/trunk/twistedcaldav/method/report_addressbook_query.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/report_addressbook_query.py	2013-06-04 19:31:04 UTC (rev 11292)
+++ CalendarServer/trunk/twistedcaldav/method/report_addressbook_query.py	2013-06-04 20:52:00 UTC (rev 11293)
@@ -246,6 +246,7 @@
                             lambda x, y: ok_resources.append((x, y)),
                             None,
                             None,
+                            None,
                             names,
                             (davxml.Read(),),
                             inherited_aces=filteredaces

Modified: CalendarServer/trunk/twistedcaldav/method/report_calendar_query.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/report_calendar_query.py	2013-06-04 19:31:04 UTC (rev 11292)
+++ CalendarServer/trunk/twistedcaldav/method/report_calendar_query.py	2013-06-04 20:52:00 UTC (rev 11293)
@@ -208,6 +208,7 @@
                     lambda x, y: ok_resources.append((x, y)),
                     None,
                     None,
+                    None,
                     names,
                     (davxml.Read(),),
                     inherited_aces=filteredaces

Modified: CalendarServer/trunk/twistedcaldav/method/report_multiget_common.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/report_multiget_common.py	2013-06-04 19:31:04 UTC (rev 11292)
+++ CalendarServer/trunk/twistedcaldav/method/report_multiget_common.py	2013-06-04 20:52:00 UTC (rev 11293)
@@ -190,12 +190,14 @@
             ok_resources = []
             bad_resources = []
             missing_resources = []
+            unavailable_resources = []
             yield self.findChildrenFaster(
                 "1",
                 request,
                 lambda x, y: ok_resources.append((x, y)),
                 lambda x, y: bad_resources.append((x, y)),
                 lambda x: missing_resources.append(x),
+                lambda x: unavailable_resources.append(x),
                 valid_names,
                 (davxml.Read(),),
                 inherited_aces=filteredaces
@@ -231,9 +233,11 @@
             for ignore_resource, href in bad_resources:
                 responses.append(davxml.StatusResponse(davxml.HRef.fromString(href), davxml.Status.fromResponseCode(responsecode.FORBIDDEN)))
 
-            # Indicate error for all missing resources
+            # Indicate error for all missing/unavailable resources
             for href in missing_resources:
                 responses.append(davxml.StatusResponse(davxml.HRef.fromString(href), davxml.Status.fromResponseCode(responsecode.NOT_FOUND)))
+            for href in unavailable_resources:
+                responses.append(davxml.StatusResponse(davxml.HRef.fromString(href), davxml.Status.fromResponseCode(responsecode.SERVICE_UNAVAILABLE)))
 
         @inlineCallbacks
         def doDirectoryAddressBookResponse():

Modified: CalendarServer/trunk/twistedcaldav/method/report_sync_collection.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/report_sync_collection.py	2013-06-04 19:31:04 UTC (rev 11292)
+++ CalendarServer/trunk/twistedcaldav/method/report_sync_collection.py	2013-06-04 20:52:00 UTC (rev 11293)
@@ -132,6 +132,7 @@
             lambda x, y: ok_resources.append((x, y)),
             lambda x, y: forbidden_resources.append((x, y)),
             None,
+            None,
             changed,
             (element.Read(),),
             inherited_aces=filteredaces

Modified: CalendarServer/trunk/twistedcaldav/resource.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/resource.py	2013-06-04 19:31:04 UTC (rev 11292)
+++ CalendarServer/trunk/twistedcaldav/resource.py	2013-06-04 20:52:00 UTC (rev 11293)
@@ -2124,7 +2124,7 @@
 
     @inlineCallbacks
     def findChildrenFaster(
-        self, depth, request, okcallback, badcallback, missingcallback,
+        self, depth, request, okcallback, badcallback, missingcallback, unavailablecallback,
         names, privileges, inherited_aces
     ):
         """
@@ -2135,7 +2135,7 @@
             yield self._newStoreHome.loadChildren()
 
         result = (yield super(CommonHomeResource, self).findChildrenFaster(
-            depth, request, okcallback, badcallback, missingcallback, names, privileges, inherited_aces
+            depth, request, okcallback, badcallback, missingcallback, unavailablecallback, names, privileges, inherited_aces
         ))
 
         returnValue(result)

Modified: CalendarServer/trunk/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/storebridge.py	2013-06-04 19:31:04 UTC (rev 11292)
+++ CalendarServer/trunk/twistedcaldav/storebridge.py	2013-06-04 20:52:00 UTC (rev 11293)
@@ -379,7 +379,7 @@
 
     @inlineCallbacks
     def findChildrenFaster(
-        self, depth, request, okcallback, badcallback, missingcallback,
+        self, depth, request, okcallback, badcallback, missingcallback, unavailablecallback,
         names, privileges, inherited_aces
     ):
         """
@@ -393,7 +393,7 @@
                 yield self._newStoreObject.objectResources()
 
         result = (yield super(_CommonHomeChildCollectionMixin, self).findChildrenFaster(
-            depth, request, okcallback, badcallback, missingcallback, names, privileges, inherited_aces
+            depth, request, okcallback, badcallback, missingcallback, unavailablecallback, names, privileges, inherited_aces
         ))
 
         returnValue(result)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130604/41bfd907/attachment-0001.html>


More information about the calendarserver-changes mailing list