[CalendarServer-changes] [3148] CalendarServer/branches/users/cdaboo/better-proxy-3132/lib-patches/ Twisted

source_changes at macosforge.org source_changes at macosforge.org
Tue Oct 14 09:29:52 PDT 2008


Revision: 3148
          http://trac.macosforge.org/projects/calendarserver/changeset/3148
Author:   cdaboo at apple.com
Date:     2008-10-14 09:29:51 -0700 (Tue, 14 Oct 2008)
Log Message:
-----------
Proper implementation of expand-property REPORT from RFC3253. Only does one level of expansion.

Modified Paths:
--------------
    CalendarServer/branches/users/cdaboo/better-proxy-3132/lib-patches/Twisted/twisted.web2.dav.resource.patch

Added Paths:
-----------
    CalendarServer/branches/users/cdaboo/better-proxy-3132/lib-patches/Twisted/twisted.web2.dav.method.report_expand.patch

Added: CalendarServer/branches/users/cdaboo/better-proxy-3132/lib-patches/Twisted/twisted.web2.dav.method.report_expand.patch
===================================================================
--- CalendarServer/branches/users/cdaboo/better-proxy-3132/lib-patches/Twisted/twisted.web2.dav.method.report_expand.patch	                        (rev 0)
+++ CalendarServer/branches/users/cdaboo/better-proxy-3132/lib-patches/Twisted/twisted.web2.dav.method.report_expand.patch	2008-10-14 16:29:51 UTC (rev 3148)
@@ -0,0 +1,214 @@
+Index: twisted/web2/dav/method/report_expand.py
+===================================================================
+--- twisted/web2/dav/method/report_expand.py	(revision 19773)
++++ twisted/web2/dav/method/report_expand.py	(working copy)
+@@ -1,6 +1,6 @@
+ # -*- test-case-name: twisted.web2.dav.test.test_report_expand -*-
+ ##
+-# Copyright (c) 2005 Apple Computer, Inc. All rights reserved.
++# Copyright (c) 2005-2008 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
+@@ -19,8 +19,6 @@
+ # 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.
+-#
+-# DRI: Wilfredo Sanchez, wsanchez at apple.com
+ ##
+ 
+ """
+@@ -29,86 +27,143 @@
+ 
+ __all__ = ["report_DAV__expand_property"]
+ 
++from twisted.internet.defer import inlineCallbacks, returnValue
+ from twisted.python import log
+ from twisted.python.failure import Failure
+-from twisted.internet.defer import deferredGenerator, waitForDeferred
+ from twisted.web2 import responsecode
+ from twisted.web2.dav import davxml
+-from twisted.web2.dav.http import statusForFailure
+ from twisted.web2.dav.davxml import dav_namespace
++from twisted.web2.dav.http import statusForFailure, MultiStatusResponse
++from twisted.web2.dav.method import prop_common
++from twisted.web2.dav.method.propfind import propertyName
++from twisted.web2.dav.resource import AccessDeniedError
++from twisted.web2.dav.util import parentForURL
++from twisted.web2.http import HTTPError, StatusResponse
+ 
++ at inlineCallbacks
+ def report_DAV__expand_property(self, request, expand_property):
+     """
+     Generate an expand-property REPORT. (RFC 3253, section 3.8)
++    
++    TODO: for simplicity we will only support one level of expansion.
+     """
+-    # FIXME: Handle depth header
+-
++    # Verify root element
+     if not isinstance(expand_property, davxml.ExpandProperty):
+         raise ValueError("%s expected as root element, not %s."
+                          % (davxml.ExpandProperty.sname(), expand_property.sname()))
+ 
++    # Only handle Depth: 0
++    depth = request.headers.getHeader("depth", "0")
++    if depth != "0":
++        log.err("Non-zero depth is not allowed: %s" % (depth,))
++        raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, "Depth %s not allowed" % (depth,)))
++    
+     #
+-    # Expand DAV:allprop
++    # Get top level properties to expand and make sure we only have one level
+     #
+     properties = {}
+ 
+     for property in expand_property.children:
+-        namespace = property.getAttribute("namespace")
+-        name      = property.getAttribute("name")
++        namespace = property.attributes.get("namespace", dav_namespace)
++        name      = property.attributes.get("name", "")
++        
++        # Make sure children have no children
++        props_to_find = []
++        for child in property.children:
++            if child.children:
++                log.err("expand-property REPORT only supports single level expansion")
++                raise HTTPError(StatusResponse(
++                    responsecode.NOT_IMPLEMENTED,
++                    "expand-property REPORT only supports single level expansion"
++                ))
++            child_namespace = child.attributes.get("namespace", dav_namespace)
++            child_name      = child.attributes.get("name", "")
++            props_to_find.append((child_namespace, child_name))
+ 
+-        if not namespace: namespace = dav_namespace
++        properties[(namespace, name)] = props_to_find
+ 
+-        if (namespace, name) == (dav_namespace, "allprop"):
+-            all_properties = waitForDeferred(self.listAllProp(request))
+-            yield all_properties
+-            all_properties = all_properties.getResult()
+-
+-            for all_property in all_properties:
+-                properties[all_property.qname()] = property
+-        else:
+-            properties[(namespace, name)] = property
+-
+     #
+-    # Look up the requested properties
++    # Generate the expanded responses status for each top-level property
+     #
+     properties_by_status = {
+         responsecode.OK        : [],
+         responsecode.NOT_FOUND : [],
+     }
++    
++    filteredaces = None
++    lastParent = None
+ 
+-    for property in properties:
+-        my_properties = waitForDeferred(self.listProperties(request))
+-        yield my_properties
+-        my_properties = my_properties.getResult()
++    for qname in properties.iterkeys():
++        try:
++            prop = (yield self.readProperty(qname, request))
++            
++            # Form the PROPFIND-style DAV:prop element we need later
++            props_to_return = davxml.PropertyContainer(*properties[qname])
+ 
+-        if property in my_properties:
+-            try:
+-                value = waitForDeferred(self.readProperty(property, request))
+-                yield value
+-                value = value.getResult()
++            # Now dereference any HRefs
++            responses = []
++            for href in prop.children:
++                if isinstance(href, davxml.HRef):
++                    
++                    # Locate the Href resource and its parent
++                    resource_uri = str(href)
++                    child = (yield request.locateResource(resource_uri))
++    
++                    if not child or not child.exists():
++                        responses.append(davxml.StatusResponse(href, davxml.Status.fromResponseCode(responsecode.NOT_FOUND)))
++                        continue
++                    parent = (yield request.locateResource(parentForURL(resource_uri)))
++    
++                    # Check privileges on parent - must have at least DAV:read
++                    try:
++                        yield parent.checkPrivileges(request, (davxml.Read(),))
++                    except AccessDeniedError:
++                        responses.append(davxml.StatusResponse(href, davxml.Status.fromResponseCode(responsecode.FORBIDDEN)))
++                        continue
++                    
++                    # Cache the last parent's inherited aces for checkPrivileges optimization
++                    if lastParent != parent:
++                        lastParent = parent
++                
++                        # Do some optimisation of access control calculation by determining any inherited ACLs outside of
++                        # the child resource loop and supply those to the checkPrivileges on each child.
++                        filteredaces = (yield parent.inheritedACEsforChildren(request))
+ 
+-                if isinstance(value, davxml.HRef):
+-                    raise NotImplementedError()
+-                else:
+-                    raise NotImplementedError()
+-            except:
+-                f = Failure()
++                    # Check privileges - must have at least DAV:read
++                    try:
++                        yield child.checkPrivileges(request, (davxml.Read(),), inherited_aces=filteredaces)
++                    except AccessDeniedError:
++                        responses.append(davxml.StatusResponse(href, davxml.Status.fromResponseCode(responsecode.FORBIDDEN)))
++                        continue
++            
++                    # Now retrieve all the requested properties on the HRef resource
++                    yield prop_common.responseForHref(
++                        request,
++                        responses,
++                        href,
++                        child,
++                        prop_common.propertyListForResource,
++                        props_to_return,
++                    )
++            
++            prop.children = responses
++            properties_by_status[responsecode.OK].append(prop)
++        except:
++            f = Failure()
+ 
+-                log.err("Error reading property %r for resource %s: %s"
+-                        % (property, self, f.value))
++            log.err("Error reading property %r for resource %s: %s" % (qname, request.uri, f.value))
+ 
+-                status = statusForFailure(f, "getting property: %s" % (property,))
+-                if status not in properties_by_status:
+-                    properties_by_status[status] = []
++            status = statusForFailure(f, "getting property: %s" % (qname,))
++            if status not in properties_by_status: properties_by_status[status] = []
++            properties_by_status[status].append(propertyName(qname))
+ 
+-                raise NotImplementedError()
++    # Build the overall response
++    propstats = [
++        davxml.PropertyStatus(
++            davxml.PropertyContainer(*properties_by_status[status]),
++            davxml.Status.fromResponseCode(status)
++        )
++        for status in properties_by_status if properties_by_status[status]
++    ]
+ 
+-                #properties_by_status[status].append(
+-                #    ____propertyName(property)
+-                #)
+-        else:
+-            properties_by_status[responsecode.NOT_FOUND].append(property)
+-
+-    raise NotImplementedError()
+-
+-report_DAV__expand_property = deferredGenerator(report_DAV__expand_property)
++    returnValue(MultiStatusResponse((davxml.PropertyStatusResponse(davxml.HRef(request.uri), *propstats),)))

Modified: CalendarServer/branches/users/cdaboo/better-proxy-3132/lib-patches/Twisted/twisted.web2.dav.resource.patch
===================================================================
--- CalendarServer/branches/users/cdaboo/better-proxy-3132/lib-patches/Twisted/twisted.web2.dav.resource.patch	2008-10-13 22:23:17 UTC (rev 3147)
+++ CalendarServer/branches/users/cdaboo/better-proxy-3132/lib-patches/Twisted/twisted.web2.dav.resource.patch	2008-10-14 16:29:51 UTC (rev 3148)
@@ -208,7 +208,22 @@
      ##
      # DAV
      ##
-@@ -570,19 +647,21 @@
+@@ -553,12 +630,13 @@
+     def supportedReports(self):
+         """
+         See L{IDAVResource.supportedReports}.
+-        This implementation lists the three main ACL reports.
++        This implementation lists the three main ACL reports and expand-property.
+         """
+         result = []
+         result.append(davxml.Report(davxml.ACLPrincipalPropSet(),))
+         result.append(davxml.Report(davxml.PrincipalMatch(),))
+         result.append(davxml.Report(davxml.PrincipalPropertySearch(),))
++        result.append(davxml.Report(davxml.ExpandProperty(),))
+         return result
+ 
+     ##
+@@ -570,19 +648,21 @@
          See L{IDAVResource.authorize}.
          """
          def onError(failure):
@@ -235,7 +250,7 @@
                      response = UnauthorizedResponse(request.credentialFactories,
                                                      request.remoteAddr)
                  else:
-@@ -593,7 +672,7 @@
+@@ -593,7 +673,7 @@
                  # class is supposed to be a FORBIDDEN status code and
                  # "Authorization will not help" according to RFC2616
                  #
@@ -244,7 +259,7 @@
  
              d = self.checkPrivileges(request, privileges, recurse)
              d.addErrback(onErrors)
-@@ -606,16 +685,21 @@
+@@ -606,16 +686,21 @@
  
      def authenticate(self, request):
          def loginSuccess(result):
@@ -270,7 +285,7 @@
  
          authHeader = request.headers.getHeader('authorization')
  
-@@ -631,9 +715,10 @@
+@@ -631,9 +716,10 @@
  
                  # Try to match principals in each principal collection on the resource
                  def gotDetails(details):
@@ -284,7 +299,7 @@
  
                  def login(pcreds):
                      d = request.portal.login(pcreds, None, *request.loginInterfaces)
-@@ -641,13 +726,15 @@
+@@ -641,13 +727,15 @@
  
                      return d
  
@@ -304,7 +319,7 @@
  
      ##
      # ACL
-@@ -656,49 +743,23 @@
+@@ -656,49 +744,23 @@
      def currentPrincipal(self, request):
          """
          @param request: the request being processed.
@@ -363,7 +378,7 @@
          """
          @return: the L{davxml.ACL} element containing the default access control
              list for this resource.
-@@ -710,6 +771,17 @@
+@@ -710,6 +772,17 @@
          #
          return readonlyACL
  
@@ -381,7 +396,7 @@
      def setAccessControlList(self, acl):
          """
          See L{IDAVResource.setAccessControlList}.
-@@ -748,13 +820,16 @@
+@@ -748,13 +821,16 @@
          # 10. Verify that new acl is not in conflict with itself
          # 11. Update acl on the resource
  
@@ -399,7 +414,7 @@
  
          # Need to get list of supported privileges
          supported = []
-@@ -773,10 +848,7 @@
+@@ -773,10 +849,7 @@
          yield supportedPrivs
          supportedPrivs = supportedPrivs.getResult()
          for item in supportedPrivs.children:
@@ -411,7 +426,7 @@
              addSupportedPrivilege(item)
  
          # Steps 1 - 6
-@@ -910,8 +982,7 @@
+@@ -910,8 +983,7 @@
          supportedPrivs = supportedPrivs.getResult()
  
          # Other principals types don't make sense as actors.
@@ -421,7 +436,7 @@
              "Principal is not an actor: %r" % (principal,)
          )
  
-@@ -1019,15 +1090,16 @@
+@@ -1019,15 +1091,16 @@
          def getMyURL():
              url = request.urlForResource(self)
  
@@ -441,7 +456,7 @@
                  "Expected %s response from readDeadProperty() exception, not %s"
                  % (responsecode.NOT_FOUND, e.response.code)
              )
-@@ -1038,9 +1110,9 @@
+@@ -1038,9 +1111,9 @@
  
              if myURL == "/":
                  # If we get to the root without any ACLs, then use the default.
@@ -453,7 +468,7 @@
  
          # Dynamically update privileges for those ace's that are inherited.
          if inheritance:
-@@ -1076,7 +1148,7 @@
+@@ -1076,7 +1149,7 @@
                                  # Adjust ACE for inherit on this resource
                                  children = list(ace.children)
                                  children.remove(TwistedACLInheritable())
@@ -462,7 +477,7 @@
                                  aces.append(davxml.ACE(*children))
              else:
                  aces.extend(inherited_aces)
-@@ -1105,8 +1177,7 @@
+@@ -1105,8 +1178,7 @@
          the child resource loop and supply those to the checkPrivileges on each child.
  
          @param request: the L{IRequest} for the request in progress.
@@ -472,7 +487,7 @@
          """
          
          # Get the parent ACLs with inheritance and preserve the <inheritable> element.
-@@ -1128,21 +1199,9 @@
+@@ -1128,21 +1200,9 @@
                  # Adjust ACE for inherit on this resource
                  children = list(ace.children)
                  children.remove(TwistedACLInheritable())
@@ -496,7 +511,7 @@
  
      inheritedACEsforChildren = deferredGenerator(inheritedACEsforChildren)
  
-@@ -1152,49 +1211,69 @@
+@@ -1152,49 +1212,69 @@
  
          This implementation returns an empty set.
          """
@@ -594,7 +609,7 @@
      def samePrincipal(self, principal1, principal2):
          """
          Check whether the two prinicpals are exactly the same in terms of
-@@ -1219,7 +1298,6 @@
+@@ -1219,7 +1299,6 @@
              return False
                  
      def matchPrincipal(self, principal1, principal2, request):
@@ -602,7 +617,7 @@
          """
          Check whether the principal1 is a principal in the set defined by
          principal2.
-@@ -1244,6 +1322,9 @@
+@@ -1244,6 +1323,9 @@
              if isinstance(principal1, davxml.Unauthenticated):
                  yield False
                  return
@@ -612,7 +627,7 @@
              else:
                  yield True
                  return
-@@ -1260,10 +1341,7 @@
+@@ -1260,10 +1342,7 @@
              yield False
              return
  
@@ -624,7 +639,7 @@
  
          principal2 = waitForDeferred(self.resolvePrincipal(principal2, request))
          yield principal2
-@@ -1271,7 +1349,6 @@
+@@ -1271,7 +1350,6 @@
  
          assert principal2 is not None, "principal2 is None"
  
@@ -632,7 +647,7 @@
          # Compare two HRefs and do group membership test as well
          if principal1 == principal2:
              yield True
-@@ -1289,6 +1366,7 @@
+@@ -1289,6 +1367,7 @@
  
      matchPrincipal = deferredGenerator(matchPrincipal)
  
@@ -640,7 +655,7 @@
      def principalIsGroupMember(self, principal1, principal2, request):
          """
          Check whether one principal is a group member of another.
-@@ -1299,18 +1377,21 @@
+@@ -1299,18 +1378,21 @@
          @return: L{Deferred} with result C{True} if principal1 is a member of principal2, C{False} otherwise
          """
          
@@ -673,7 +688,7 @@
          
      def validPrincipal(self, ace_principal, request):
          """
-@@ -1351,11 +1432,16 @@
+@@ -1351,11 +1433,16 @@
          @return C{True} if C{href_principal} is valid, C{False} otherwise.
  
          This implementation tests for a href element that corresponds to
@@ -693,7 +708,7 @@
          return d
  
      def resolvePrincipal(self, principal, request):
-@@ -1404,8 +1490,7 @@
+@@ -1404,8 +1491,7 @@
              try:
                  principal = principal.getResult()
              except HTTPError, e:
@@ -703,7 +718,7 @@
                      "Expected %s response from readProperty() exception, not %s"
                      % (responsecode.NOT_FOUND, e.response.code)
                  )
-@@ -1432,15 +1517,15 @@
+@@ -1432,15 +1518,15 @@
                  log.err("DAV:self ACE is set on non-principal resource %r" % (self,))
                  yield None
                  return
@@ -722,7 +737,7 @@
              "Not a meta-principal: %r" % (principal,)
          )
  
-@@ -1517,6 +1602,270 @@
+@@ -1517,6 +1603,270 @@
          return None
  
      ##
@@ -993,7 +1008,7 @@
      # HTTP
      ##
  
-@@ -1525,15 +1874,11 @@
+@@ -1525,15 +1875,11 @@
          #litmus = request.headers.getRawHeaders("x-litmus")
          #if litmus: log.msg("*** Litmus test: %s ***" % (litmus,))
  
@@ -1011,7 +1026,7 @@
  
          def setHeaders(response):
              response = IResponse(response)
-@@ -1567,7 +1912,7 @@
+@@ -1567,7 +1913,7 @@
      def findChildren(self, depth, request, callback, privileges=None, inherited_aces=None):
          return succeed(None)
  
@@ -1020,7 +1035,7 @@
      """
      Resource representing a WebDAV principal.  (RFC 3744, section 2)
      """
-@@ -1577,7 +1922,7 @@
+@@ -1577,7 +1923,7 @@
      # WebDAV
      ##
  
@@ -1029,7 +1044,7 @@
          (dav_namespace, "alternate-URI-set"),
          (dav_namespace, "principal-URL"    ),
          (dav_namespace, "group-member-set" ),
-@@ -1585,14 +1930,11 @@
+@@ -1585,14 +1931,11 @@
      )
  
      def davComplianceClasses(self):
@@ -1045,7 +1060,7 @@
      def readProperty(self, property, request):
          def defer():
              if type(property) is tuple:
-@@ -1610,10 +1952,20 @@
+@@ -1610,10 +1953,20 @@
                      return davxml.PrincipalURL(davxml.HRef(self.principalURL()))
  
                  if name == "group-member-set":
@@ -1068,7 +1083,7 @@
  
                  if name == "resourcetype":
                      if self.isCollection():
-@@ -1655,7 +2007,7 @@
+@@ -1655,7 +2008,7 @@
          principals.  Subclasses should override this method to provide member
          URLs for this resource if appropriate.
          """
@@ -1077,7 +1092,7 @@
  
      def groupMemberships(self):
          """
-@@ -1666,6 +2018,7 @@
+@@ -1666,6 +2019,7 @@
          """
          unimplemented(self)
  
@@ -1085,7 +1100,7 @@
      def principalMatch(self, href):
          """
          Check whether the supplied principal matches this principal or is a
-@@ -1675,10 +2028,33 @@
+@@ -1675,10 +2029,33 @@
          """
          uri = str(href)
          if self.principalURL() == uri:
@@ -1121,7 +1136,7 @@
  class AccessDeniedError(Exception):
      def __init__(self, errors):
          """ 
-@@ -1718,6 +2094,37 @@
+@@ -1718,6 +2095,37 @@
  davxml.registerElement(TwistedACLInheritable)
  davxml.ACE.allowed_children[(twisted_dav_namespace, "inheritable")] = (0, 1)
  
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20081014/0adcbe75/attachment-0001.html 


More information about the calendarserver-changes mailing list