[CalendarServer-changes] [2310] CalendarServer/branches/release/CalendarServer-1.3-dev

source_changes at macosforge.org source_changes at macosforge.org
Mon Apr 14 12:09:54 PDT 2008


Revision: 2310
          http://trac.macosforge.org/projects/calendarserver/changeset/2310
Author:   cdaboo at apple.com
Date:     2008-04-14 12:09:53 -0700 (Mon, 14 Apr 2008)

Log Message:
-----------
Puller-up r2303.

Modified Paths:
--------------
    CalendarServer/branches/release/CalendarServer-1.3-dev/lib-patches/Twisted/twisted.web2.dav.resource.patch
    CalendarServer/branches/release/CalendarServer-1.3-dev/twistedcaldav/extensions.py
    CalendarServer/branches/release/CalendarServer-1.3-dev/twistedcaldav/method/__init__.py
    CalendarServer/branches/release/CalendarServer-1.3-dev/twistedcaldav/static.py

Added Paths:
-----------
    CalendarServer/branches/release/CalendarServer-1.3-dev/twistedcaldav/method/propfind.py

Modified: CalendarServer/branches/release/CalendarServer-1.3-dev/lib-patches/Twisted/twisted.web2.dav.resource.patch
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.3-dev/lib-patches/Twisted/twisted.web2.dav.resource.patch	2008-04-14 18:52:19 UTC (rev 2309)
+++ CalendarServer/branches/release/CalendarServer-1.3-dev/lib-patches/Twisted/twisted.web2.dav.resource.patch	2008-04-14 19:09:53 UTC (rev 2310)
@@ -382,17 +382,42 @@
                                  aces.append(davxml.ACE(*children))
              else:
                  aces.extend(inherited_aces)
-@@ -1128,7 +1200,7 @@
+@@ -1105,8 +1177,7 @@
+         the child resource loop and supply those to the checkPrivileges on each child.
+ 
+         @param request: the L{IRequest} for the request in progress.
+-        @return:        a C{list} of L{Ace}s that child resources of this one will
+-            inherit and which will match the currently authenticated principal.
++        @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.
+@@ -1128,21 +1199,9 @@
                  # Adjust ACE for inherit on this resource
                  children = list(ace.children)
                  children.remove(TwistedACLInheritable())
 -                children.append(davxml.Inherited(davxml.HRef.fromString(request.urlForResource(self))))
 +                children.append(davxml.Inherited(davxml.HRef(request.urlForResource(self))))
                  aces.append(davxml.ACE(*children))
-                 
-         # Filter out those that do not have a principal match with the current principal
-@@ -1152,49 +1224,69 @@
+-                
+-        # Filter out those that do not have a principal match with the current principal
+-        principal = self.currentPrincipal(request)
+-        filteredaces = []
+-        for ace in aces:
+-            if self.matchPrincipal(principal, ace.principal, request):
+-                if ace.invert:
+-                    continue
+-            else:
+-                if not ace.invert:
+-                    continue
+-            filteredaces.append(ace)
+-        yield filteredaces
++        yield aces
  
+     inheritedACEsforChildren = deferredGenerator(inheritedACEsforChildren)
+ 
+@@ -1152,49 +1211,69 @@
+ 
          This implementation returns an empty set.
          """
 -
@@ -489,7 +514,7 @@
      def samePrincipal(self, principal1, principal2):
          """
          Check whether the two prinicpals are exactly the same in terms of
-@@ -1219,7 +1311,6 @@
+@@ -1219,7 +1298,6 @@
              return False
                  
      def matchPrincipal(self, principal1, principal2, request):
@@ -497,7 +522,7 @@
          """
          Check whether the principal1 is a principal in the set defined by
          principal2.
-@@ -1244,6 +1335,9 @@
+@@ -1244,6 +1322,9 @@
              if isinstance(principal1, davxml.Unauthenticated):
                  yield False
                  return
@@ -507,7 +532,7 @@
              else:
                  yield True
                  return
-@@ -1271,7 +1365,6 @@
+@@ -1271,7 +1352,6 @@
  
          assert principal2 is not None, "principal2 is None"
  
@@ -515,7 +540,7 @@
          # Compare two HRefs and do group membership test as well
          if principal1 == principal2:
              yield True
-@@ -1302,9 +1395,9 @@
+@@ -1302,9 +1382,9 @@
          def testGroup(group):
              # Get principal resource for principal2
              if group and isinstance(group, DAVPrincipalResource):
@@ -528,7 +553,7 @@
                  
              return False
  
-@@ -1351,11 +1444,16 @@
+@@ -1351,11 +1431,16 @@
          @return C{True} if C{href_principal} is valid, C{False} otherwise.
  
          This implementation tests for a href element that corresponds to
@@ -548,7 +573,7 @@
          return d
  
      def resolvePrincipal(self, principal, request):
-@@ -1432,7 +1530,7 @@
+@@ -1432,7 +1517,7 @@
                  log.err("DAV:self ACE is set on non-principal resource %r" % (self,))
                  yield None
                  return
@@ -557,7 +582,7 @@
  
          if isinstance(principal, davxml.HRef):
              yield principal
-@@ -1517,6 +1615,270 @@
+@@ -1517,6 +1602,270 @@
          return None
  
      ##
@@ -828,7 +853,7 @@
      # HTTP
      ##
  
-@@ -1567,7 +1929,7 @@
+@@ -1567,7 +1916,7 @@
      def findChildren(self, depth, request, callback, privileges=None, inherited_aces=None):
          return succeed(None)
  
@@ -837,7 +862,7 @@
      """
      Resource representing a WebDAV principal.  (RFC 3744, section 2)
      """
-@@ -1577,7 +1939,7 @@
+@@ -1577,7 +1926,7 @@
      # WebDAV
      ##
  
@@ -846,7 +871,7 @@
          (dav_namespace, "alternate-URI-set"),
          (dav_namespace, "principal-URL"    ),
          (dav_namespace, "group-member-set" ),
-@@ -1585,14 +1947,11 @@
+@@ -1585,14 +1934,11 @@
      )
  
      def davComplianceClasses(self):
@@ -862,7 +887,7 @@
      def readProperty(self, property, request):
          def defer():
              if type(property) is tuple:
-@@ -1610,10 +1969,10 @@
+@@ -1610,10 +1956,10 @@
                      return davxml.PrincipalURL(davxml.HRef(self.principalURL()))
  
                  if name == "group-member-set":
@@ -875,7 +900,7 @@
  
                  if name == "resourcetype":
                      if self.isCollection():
-@@ -1677,8 +2036,27 @@
+@@ -1677,8 +2023,27 @@
          if self.principalURL() == uri:
              return True
          else:
@@ -904,7 +929,7 @@
  class AccessDeniedError(Exception):
      def __init__(self, errors):
          """ 
-@@ -1718,6 +2096,37 @@
+@@ -1718,6 +2083,37 @@
  davxml.registerElement(TwistedACLInheritable)
  davxml.ACE.allowed_children[(twisted_dav_namespace, "inheritable")] = (0, 1)
  

Modified: CalendarServer/branches/release/CalendarServer-1.3-dev/twistedcaldav/extensions.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.3-dev/twistedcaldav/extensions.py	2008-04-14 18:52:19 UTC (rev 2309)
+++ CalendarServer/branches/release/CalendarServer-1.3-dev/twistedcaldav/extensions.py	2008-04-14 19:09:53 UTC (rev 2310)
@@ -83,10 +83,10 @@
     def authorizationPrincipal(self, request, authid, authnPrincipal):
         """
         Determine the authorization principal for the given request and authentication principal.
-        This implementation looks for an X-Authorize-As header value to use as the authoization principal.
+        This implementation looks for an X-Authorize-As header value to use as the authorization principal.
         
         @param request: the L{IRequest} for the request in progress.
-        @param authid: a string containing the uthentication/authorization identifier
+        @param authid: a string containing the authentication/authorization identifier
             for the principal to lookup.
         @param authnPrincipal: the L{IDAVPrincipal} for the authenticated principal
         @return: a deferred result C{tuple} of (L{IDAVPrincipal}, C{str}) containing the authorization principal
@@ -176,6 +176,7 @@
 
         if depth == "0" or not self.isCollection():
             yield None
+            return
 
         # First find all depth 1 children
         #children = []
@@ -230,10 +231,13 @@
                     for resource, url in items[2]:
                         badcallback(resource, url)
 
-        # TODO: Depth: inifinity support
+        # TODO: Depth: infinity support
         if depth == "infinity":
             for collection, url in allowed_collections:
-                d = waitForDeferred(collection.findChildrenFaster(request, depth, okcallback, badcallback, names, privileges, inherited_aces=None))
+                collection_inherited_aces = waitForDeferred(collection.inheritedACEsforChildren(request))
+                yield collection_inherited_aces
+                collection_inherited_aces = collection_inherited_aces.getResult()
+                d = waitForDeferred(collection.findChildrenFaster(depth, request, okcallback, badcallback, names, privileges, inherited_aces=collection_inherited_aces))
                 yield d
                 d.getResult()
                 
@@ -243,6 +247,10 @@
 
     def checkACLPrivilege(self, request, acl, privyset, privileges, inherited_aces):
         
+        if acl is None:
+            yield False
+            return
+
         principal = self.currentPrincipal(request)
 
         # Other principal types don't make sense as actors.
@@ -318,7 +326,30 @@
 
         return acl
     
+    @deferredGenerator
+    def matchPrincipal(self, principal1, principal2, request):
+        """
+        Implementation of DAVResource.matchPrincipal that caches the principal match
+        for the duration of a request. This avoids having to do repeated group membership
+        tests when privileges on multiple resources are determined.
+        """
+        
+        if not hasattr(request, "matchPrincipalCache"):
+            request.matchPrincipalCache = {}
 
+        # The interesting part of a principal is it's one child
+        principals = (principal1, principal2)
+        cache_key = tuple([str(p.children[0]) for p in principals])
+
+        match = request.matchPrincipalCache.get(cache_key, None)
+        if match is None:
+            match = waitForDeferred(super(DAVResource, self).matchPrincipal(principal1, principal2, request))
+            yield match
+            match = match.getResult()
+            request.matchPrincipalCache[cache_key] = match
+            
+        yield match
+
 class DAVPrincipalResource (SuperDAVPrincipalResource):
     """
     Extended L{twisted.web2.dav.static.DAVFile} implementation.
@@ -335,7 +366,7 @@
         return super(DAVPrincipalResource, self).readProperty(property, request)
 
     def resourceType(self):
-        # Allow live property to be overriden by dead property
+        # Allow live property to be overridden by dead property
         if self.deadProperties().contains((dav_namespace, "resourcetype")):
             return self.deadProperties().get((dav_namespace, "resourcetype"))
         if self.isCollection():

Modified: CalendarServer/branches/release/CalendarServer-1.3-dev/twistedcaldav/method/__init__.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.3-dev/twistedcaldav/method/__init__.py	2008-04-14 18:52:19 UTC (rev 2309)
+++ CalendarServer/branches/release/CalendarServer-1.3-dev/twistedcaldav/method/__init__.py	2008-04-14 19:09:53 UTC (rev 2310)
@@ -29,6 +29,7 @@
     "get",
     "mkcalendar",
     "mkcol",
+    "propfind",
     "put",
     "report",
     "report_calquery",

Copied: CalendarServer/branches/release/CalendarServer-1.3-dev/twistedcaldav/method/propfind.py (from rev 2303, CalendarServer/trunk/twistedcaldav/method/propfind.py)
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.3-dev/twistedcaldav/method/propfind.py	                        (rev 0)
+++ CalendarServer/branches/release/CalendarServer-1.3-dev/twistedcaldav/method/propfind.py	2008-04-14 19:09:53 UTC (rev 2310)
@@ -0,0 +1,209 @@
+# -*- test-case-name: twisted.web2.dav.test.test_prop.PROP.test_PROPFIND -*-
+##
+# Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# 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
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+##
+
+"""
+WebDAV PROPFIND method
+"""
+
+__all__ = ["http_PROPFIND"]
+
+from twisted.python import log
+from twisted.python.failure import Failure
+from twisted.internet.defer import deferredGenerator, waitForDeferred
+from twisted.web2.http import HTTPError
+from twisted.web2 import responsecode
+from twisted.web2.http import StatusResponse
+from twisted.web2.dav import davxml
+from twisted.web2.dav.http import MultiStatusResponse, statusForFailure
+from twisted.web2.dav.util import normalizeURL, davXMLFromStream
+
+"""
+This is a direct copy of the twisted implementation of PROPFIND except that it uses the
+findChildrenFaster method to optimize child privilege checking.
+"""
+
+def http_PROPFIND(self, request):
+    """
+    Respond to a PROPFIND request. (RFC 2518, section 8.1)
+    """
+    if not self.exists():
+        log.err("File not found: %s" % (self.fp.path,))
+        raise HTTPError(responsecode.NOT_FOUND)
+
+    #
+    # Check authentication and access controls
+    #
+    x = waitForDeferred(self.authorize(request, (davxml.Read(),)))
+    yield x
+    x.getResult()
+
+    #
+    # Read request body
+    #
+    try:
+        doc = waitForDeferred(davXMLFromStream(request.stream))
+        yield doc
+        doc = doc.getResult()
+    except ValueError, e:
+        log.err("Error while handling PROPFIND body: %s" % (e,))
+        raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, str(e)))
+
+    if doc is None:
+        # No request body means get all properties.
+        search_properties = "all"
+    else:
+        #
+        # Parse request
+        #
+        find = doc.root_element
+        if not isinstance(find, davxml.PropertyFind):
+            error = ("Non-%s element in PROPFIND request body: %s"
+                     % (davxml.PropertyFind.sname(), find))
+            log.err(error)
+            raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, error))
+
+        container = find.children[0]
+
+        if isinstance(container, davxml.AllProperties):
+            # Get all properties
+            search_properties = "all"
+        elif isinstance(container, davxml.PropertyName):
+            # Get names only
+            search_properties = "names"
+        elif isinstance(container, davxml.PropertyContainer):
+            properties = container.children
+            search_properties = [(p.namespace, p.name) for p in properties]
+        else:
+            raise AssertionError("Unexpected element type in %s: %s"
+                                 % (davxml.PropertyFind.sname(), container))
+
+    #
+    # Generate XML output stream
+    #
+    request_uri = request.uri
+    depth = request.headers.getHeader("depth", "infinity")
+
+    xml_responses = []
+
+    # FIXME: take advantage of the new generative properties of findChildren
+
+    my_url = normalizeURL(request_uri)
+    if self.isCollection() and not my_url.endswith("/"):
+        my_url += "/"
+
+    # Do some optimization of access control calculation by determining any inherited ACLs outside of
+    # the child resource loop and supply those to the checkPrivileges on each child.
+    filtered_aces = waitForDeferred(self.inheritedACEsforChildren(request))
+    yield filtered_aces
+    filtered_aces = filtered_aces.getResult()
+
+    resources = [(self, my_url)]
+
+    d = self.findChildrenFaster(depth, request, lambda x, y: resources.append((x, y)), None, None, (davxml.Read(),), inherited_aces=filtered_aces)
+    x = waitForDeferred(d)
+    yield x
+    x.getResult()
+
+    for resource, uri in resources:
+        if search_properties is "names":
+            try:
+                resource_properties = waitForDeferred(resource.listProperties(request))
+                yield resource_properties
+                resource_properties = resource_properties.getResult()
+            except:
+                log.err("Unable to get properties for resource %r" % (resource,))
+                raise
+
+            properties_by_status = {
+                responsecode.OK: [propertyName(p) for p in resource_properties]
+            }
+        else:
+            properties_by_status = {
+                responsecode.OK        : [],
+                responsecode.NOT_FOUND : [],
+            }
+
+            if search_properties is "all":
+                properties_to_enumerate = waitForDeferred(resource.listAllprop(request))
+                yield properties_to_enumerate
+                properties_to_enumerate = properties_to_enumerate.getResult()
+            else:
+                properties_to_enumerate = search_properties
+
+            for property in properties_to_enumerate:
+                has = waitForDeferred(resource.hasProperty(property, request))
+                yield has
+                has = has.getResult()
+                if has:
+                    try:
+                        resource_property = waitForDeferred(resource.readProperty(property, request))
+                        yield resource_property
+                        resource_property = resource_property.getResult()
+                    except:
+                        f = Failure()
+
+                        log.err("Error reading property %r for resource %s: %s" % (property, uri, f.value))
+
+                        status = statusForFailure(f, "getting property: %s" % (property,))
+                        if status not in properties_by_status:
+                            properties_by_status[status] = []
+                        properties_by_status[status].append(propertyName(property))
+                    else:
+                        properties_by_status[responsecode.OK].append(resource_property)
+                else:
+                    properties_by_status[responsecode.NOT_FOUND].append(propertyName(property))
+
+        propstats = []
+
+        for status in properties_by_status:
+            properties = properties_by_status[status]
+            if not properties: continue
+
+            xml_status    = davxml.Status.fromResponseCode(status)
+            xml_container = davxml.PropertyContainer(*properties)
+            xml_propstat  = davxml.PropertyStatus(xml_container, xml_status)
+
+            propstats.append(xml_propstat)
+
+        xml_resource = davxml.HRef(uri)
+        xml_response = davxml.PropertyStatusResponse(xml_resource, *propstats)
+
+        xml_responses.append(xml_response)
+
+    #
+    # Return response
+    #
+    yield MultiStatusResponse(xml_responses)
+
+http_PROPFIND = deferredGenerator(http_PROPFIND)
+
+##
+# Utilities
+##
+
+def propertyName(name):
+    property_namespace, property_name = name
+    pname = davxml.WebDAVUnknownElement()
+    pname.namespace = property_namespace
+    pname.name = property_name
+    return pname

Modified: CalendarServer/branches/release/CalendarServer-1.3-dev/twistedcaldav/static.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.3-dev/twistedcaldav/static.py	2008-04-14 18:52:19 UTC (rev 2309)
+++ CalendarServer/branches/release/CalendarServer-1.3-dev/twistedcaldav/static.py	2008-04-14 19:09:53 UTC (rev 2310)
@@ -840,4 +840,5 @@
 twistedcaldav.method.delete.CalDAVFile   = CalDAVFile
 twistedcaldav.method.get.CalDAVFile      = CalDAVFile
 twistedcaldav.method.mkcol.CalDAVFile    = CalDAVFile
+twistedcaldav.method.propfind.CalDAVFile = CalDAVFile
 twistedcaldav.method.put.CalDAVFile      = CalDAVFile

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20080414/7e73a75e/attachment-0001.html


More information about the calendarserver-changes mailing list