[CalendarServer-changes] [623] CalendarServer/branches/users/wsanchez/provisioning-2

source_changes at macosforge.org source_changes at macosforge.org
Thu Nov 30 15:08:21 PST 2006


Revision: 623
          http://trac.macosforge.org/projects/calendarserver/changeset/623
Author:   wsanchez at apple.com
Date:     2006-11-30 15:08:20 -0800 (Thu, 30 Nov 2006)

Log Message:
-----------
Clean up principal collections API some more.

Modified Paths:
--------------
    CalendarServer/branches/users/wsanchez/provisioning-2/conf/repository.xml
    CalendarServer/branches/users/wsanchez/provisioning-2/lib-patches/Twisted/twisted.web2.dav.idav.patch
    CalendarServer/branches/users/wsanchez/provisioning-2/lib-patches/Twisted/twisted.web2.dav.resource.patch
    CalendarServer/branches/users/wsanchez/provisioning-2/lib-patches/Twisted/twisted.web2.dav.static.patch
    CalendarServer/branches/users/wsanchez/provisioning-2/lib-patches/Twisted/twisted.web2.dav.test.test_resource.patch
    CalendarServer/branches/users/wsanchez/provisioning-2/support/CalendarServer.tmproj
    CalendarServer/branches/users/wsanchez/provisioning-2/support/patchmaker.py
    CalendarServer/branches/users/wsanchez/provisioning-2/twistedcaldav/directory/principal.py
    CalendarServer/branches/users/wsanchez/provisioning-2/twistedcaldav/directory/test/test_principal.py
    CalendarServer/branches/users/wsanchez/provisioning-2/twistedcaldav/resource.py
    CalendarServer/branches/users/wsanchez/provisioning-2/twistedcaldav/static.py
    CalendarServer/branches/users/wsanchez/provisioning-2/twistedcaldav/test/test_mkcalendar.py

Modified: CalendarServer/branches/users/wsanchez/provisioning-2/conf/repository.xml
===================================================================
--- CalendarServer/branches/users/wsanchez/provisioning-2/conf/repository.xml	2006-11-29 03:07:23 UTC (rev 622)
+++ CalendarServer/branches/users/wsanchez/provisioning-2/conf/repository.xml	2006-11-30 23:08:20 UTC (rev 623)
@@ -26,7 +26,7 @@
       <properties>
         <acl>
           <ace>
-            <principal><authenticated/></principal>
+            <principal><all/></principal>
             <grant><privilege><read/></privilege></grant>
           </ace>
           <ace>

Modified: CalendarServer/branches/users/wsanchez/provisioning-2/lib-patches/Twisted/twisted.web2.dav.idav.patch
===================================================================
--- CalendarServer/branches/users/wsanchez/provisioning-2/lib-patches/Twisted/twisted.web2.dav.idav.patch	2006-11-29 03:07:23 UTC (rev 622)
+++ CalendarServer/branches/users/wsanchez/provisioning-2/lib-patches/Twisted/twisted.web2.dav.idav.patch	2006-11-30 23:08:20 UTC (rev 623)
@@ -20,7 +20,26 @@
          @return: An L{Deferred} that fires when all the children have been found
          """
  
-@@ -180,6 +182,80 @@
+@@ -125,15 +127,10 @@
+             L{responsecode.UNAUTHORIZED}) if not authorized.
+         """
+ 
+-    def principalCollections(request):
++    def principalCollections():
+         """
+-        Provides the DAV:HRef's of collection resources which contain principal
+-        resources which may be used in access control entries on this resource.
+-        (RFC 3744, section 5.8)
+-        @param request: the request being processed.
+-        @return: a deferred sequence of L{davxml.HRef}s referring to
+-            collection resources which implement the
+-            C{DAV:principal-property-search} C{REPORT}.
++        @return: an interable of L{IDAVPrincipalCollectionResource}s which
++            contain principals used in ACLs for this resource.
+         """
+ 
+     def setAccessControlList(acl):
+@@ -180,6 +177,80 @@
              the specified principal.
          """
  
@@ -101,3 +120,18 @@
  class IDAVPrincipalResource (IDAVResource):
      """
      WebDAV principal resource.  (RFC 3744, section 2)
+@@ -212,3 +283,14 @@
+         directly a member.  (RFC 3744, section 4.4)
+         @return: a iterable of group principal URLs.
+         """
++
++class IDAVPrincipalCollectionResource (IDAVResource):
++    """
++    WebDAV principal collection resource.  (RFC 3744, section 5.8)
++    """
++    def principalCollectionURL():
++        """
++        Provides a URL for this resource which may be used to identify this
++        resource in ACL requests.  (RFC 3744, section 5.8)
++        @return: a URL.
++        """

Modified: CalendarServer/branches/users/wsanchez/provisioning-2/lib-patches/Twisted/twisted.web2.dav.resource.patch
===================================================================
--- CalendarServer/branches/users/wsanchez/provisioning-2/lib-patches/Twisted/twisted.web2.dav.resource.patch	2006-11-29 03:07:23 UTC (rev 622)
+++ CalendarServer/branches/users/wsanchez/provisioning-2/lib-patches/Twisted/twisted.web2.dav.resource.patch	2006-11-30 23:08:20 UTC (rev 623)
@@ -2,8 +2,18 @@
 ===================================================================
 --- twisted/web2/dav/resource.py	(revision 18545)
 +++ twisted/web2/dav/resource.py	(working copy)
-@@ -44,6 +44,8 @@
+@@ -40,10 +40,18 @@
+     "unauthenticatedPrincipal",
+ ]
  
++import __builtin__
++if not hasattr(__builtin__, "set"):
++    import sets.Set as set
++if not hasattr(__builtin__, "frozenset"):
++    import sets.ImmutableSet as frozenset
++
+ import urllib
+ 
  from zope.interface import implements
  from twisted.python import log
 +from twisted.python.failure import Failure
@@ -11,7 +21,16 @@
  from twisted.internet.defer import Deferred, maybeDeferred, succeed
  from twisted.internet.defer import waitForDeferred, deferredGenerator
  from twisted.internet import reactor
-@@ -130,6 +132,8 @@
+@@ -57,7 +65,7 @@
+ from twisted.web2.dav import davxml
+ from twisted.web2.dav.davxml import dav_namespace, lookupElement
+ from twisted.web2.dav.davxml import twisted_dav_namespace, twisted_private_namespace
+-from twisted.web2.dav.idav import IDAVResource, IDAVPrincipalResource
++from twisted.web2.dav.idav import IDAVResource, IDAVPrincipalResource, IDAVPrincipalCollectionResource
+ from twisted.web2.dav.http import NeedPrivilegesResponse
+ from twisted.web2.dav.noneprops import NonePropertyStore
+ from twisted.web2.dav.util import unimplemented, parentForURL, joinURL
+@@ -130,6 +138,8 @@
          (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
@@ -20,7 +39,7 @@
  
          (twisted_dav_namespace, "resource-class"),
      )
-@@ -166,6 +170,14 @@
+@@ -166,6 +176,14 @@
          if qname[0] == twisted_private_namespace:
              return succeed(False)
  
@@ -35,16 +54,34 @@
          return succeed(qname in self.liveProperties or self.deadProperties().contains(qname))
  
      def readProperty(self, property, request):
-@@ -253,7 +265,7 @@
+@@ -239,8 +257,10 @@
+                     )
  
+                 if name == "supported-report-set":
+-                    supported = [davxml.SupportedReport(report,) for report in self.supportedReports()]
+-                    return davxml.SupportedReportSet(*supported)
++                    return davxml.SupportedReportSet(*[
++                        davxml.SupportedReport(report,)
++                        for report in self.supportedReports()
++                    ])
+ 
+                 if name == "supported-privilege-set":
+                     return self.supportedPrivileges(request)
+@@ -252,9 +272,10 @@
+                     return davxml.InheritedACLSet(*self.inheritedACLSet())
+ 
                  if name == "principal-collection-set":
-                     d = self.principalCollections(request)
+-                    d = self.principalCollections(request)
 -                    d.addCallback(lambda collections: davxml.PrincipalCollectionSet(*collections))
-+                    d.addCallback(lambda collections: davxml.PrincipalCollectionSet(*[davxml.HRef.fromString(uri) for uri in collections]))
-                     return d
+-                    return d
++                    return davxml.PrincipalCollectionSet(*[
++                        davxml.HRef(principalCollection.principalCollectionURI())
++                        for principalCollection in self.principalCollections()
++                    ])
  
                  def ifAllowed(privileges, callback):
-@@ -286,7 +298,33 @@
+                     def onError(failure):
+@@ -286,7 +307,33 @@
                          d.addCallback(gotACL)
                          return d
                      return ifAllowed((davxml.ReadACL(),), callback)
@@ -78,9 +115,13 @@
              elif namespace == twisted_dav_namespace:
                  if name == "resource-class":
                      class ResourceClass (davxml.WebDAVTextElement):
-@@ -366,12 +404,26 @@
-         # FIXME: A set would be better here, that that's a python 2.4+ feature.
-         qnames = list(self.liveProperties)
+@@ -363,15 +410,28 @@
+         """
+         See L{IDAVResource.listProperties}.
+         """
+-        # FIXME: A set would be better here, that that's a python 2.4+ feature.
+-        qnames = list(self.liveProperties)
++        qnames = set(self.liveProperties)
  
 +        # Add dynamic live properties that exist
 +        dynamicLiveProperties = (
@@ -106,7 +147,30 @@
      def listAllprop(self, request):
          """
          Some DAV properties should not be returned to a C{DAV:allprop} query.
-@@ -509,6 +561,9 @@
+@@ -465,8 +525,22 @@
+             return super(DAVPropertyMixIn, self).displayName()
+ 
+ class DAVResource (DAVPropertyMixIn, StaticRenderMixin):
++    """
++    WebDAV resource.
++    """
+     implements(IDAVResource)
+ 
++    def __init__(self, principalCollections=None):
++        """
++        @param principalCollections: an iterable of L{IDAVPrincipalCollectionResource}s
++            which contain principals to be used in ACLs for this resource.
++        """
++        if principalCollections is not None:
++            self._principalCollections = frozenset([
++                IDAVPrincipalCollectionResource(principalCollection)
++                for principalCollection in principalCollections
++            ])
++
+     ##
+     # DAV
+     ##
+@@ -509,6 +583,9 @@
              reactor.callLater(0, getChild)
  
          def checkPrivileges(child):
@@ -116,7 +180,7 @@
              if privileges is None:
                  return child
     
-@@ -517,14 +572,17 @@
+@@ -517,14 +594,17 @@
              return d
  
          def gotChild(child, childpath):
@@ -141,7 +205,7 @@
  
              reactor.callLater(0, getChild)
  
-@@ -535,10 +593,10 @@
+@@ -535,10 +615,10 @@
                  completionDeferred.callback(None)
              else:
                  childpath = joinURL(basepath, childname)
@@ -156,7 +220,7 @@
  
          getChild()
  
-@@ -564,19 +622,21 @@
+@@ -564,19 +644,21 @@
          See L{IDAVResource.authorize}.
          """
          def onError(failure):
@@ -183,7 +247,7 @@
                      response = UnauthorizedResponse(request.credentialFactories,
                                                      request.remoteAddr)
                  else:
-@@ -587,7 +647,7 @@
+@@ -587,7 +669,7 @@
                  # class is supposed to be a FORBIDDEN status code and
                  # "Authorization will not help" according to RFC2616
                  #
@@ -192,7 +256,7 @@
  
              d = self.checkPrivileges(request, privileges, recurse)
              d.addErrback(onErrors)
-@@ -600,16 +660,22 @@
+@@ -600,16 +682,21 @@
  
      def authenticate(self, request):
          def loginSuccess(result):
@@ -201,7 +265,6 @@
 +            """
 +            @param result: returned tuple from auth.DAVRealm.requestAvatar.
 +            """
-+            
 +            request.authnUser = result[1]
 +            request.authzUser = result[2]
 +            return (request.authnUser, request.authzUser,)
@@ -219,7 +282,7 @@
  
          authHeader = request.headers.getHeader('authorization')
  
-@@ -625,9 +691,10 @@
+@@ -625,9 +712,10 @@
  
                  # Try to match principals in each principal collection on the resource
                  def gotDetails(details):
@@ -233,7 +296,7 @@
  
                  def login(pcreds):
                      d = request.portal.login(pcreds, None, *request.loginInterfaces)
-@@ -635,13 +702,15 @@
+@@ -635,13 +723,15 @@
  
                      return d
  
@@ -253,7 +316,7 @@
  
      ##
      # ACL
-@@ -650,10 +719,10 @@
+@@ -650,49 +740,23 @@
      def currentPrincipal(self, request):
          """
          @param request: the request being processed.
@@ -267,44 +330,37 @@
          else:
              return unauthenticatedPrincipal
  
-@@ -666,33 +735,28 @@
-         present on this resource, it tries to get it from the parent, unless it
-         is the root or has no parent.
+-    def principalCollections(self, request):
++    def principalCollections(self):
          """
+         See L{IDAVResource.accessControlList}.
+-
+-        This implementation tries to read the L{davxml.PrincipalCollectionSet}
+-        from the dead property store of this resource and uses that. If not
+-        present on this resource, it tries to get it from the parent, unless it
+-        is the root or has no parent.
+         """
 -        try:
 -            principalCollections = self.readDeadProperty(davxml.PrincipalCollectionSet).childrenOfType(davxml.HRef)
 -        except HTTPError, e:
 -            if e.response.code != responsecode.NOT_FOUND:
 -                raise
-+        if self.hasDeadProperty(davxml.PrincipalCollectionSet):
-+            return succeed([
-+                str(href) for href in
-+                self.readDeadProperty(davxml.PrincipalCollectionSet).childrenOfType(davxml.HRef)
-+            ])
++        if hasattr(self, "_principalCollections"):
++            return self._principalCollections
++        else:
++            return ()
  
 -            principalCollections = []
-+        myURL = request.urlForResource(self)
-+        assert myURL is not None, "Resource %s was not looked up via request" % (self,)
-+        if myURL == "/":
-+            return succeed(())
- 
+-
 -            # Try the parent
 -            myURL = request.urlForResource(self)
 -            if myURL != "/":
 -                parentURL = parentForURL(myURL)
-+        def gotParent(parent):
-+            if parent is None:
-+                return ()
-+            else:
-+                return parent.principalCollections(request)
- 
+-
 -                parent = waitForDeferred(request.locateResource(parentURL))
 -                yield parent
 -                parent = parent.getResult()
-+        d = request.locateResource(parentForURL(myURL))
-+        d.addCallback(gotParent)
-+        return d
- 
+-
 -                if parent:
 -                    principalCollections = waitForDeferred(parent.principalCollections(request))
 -                    yield principalCollections
@@ -349,7 +405,25 @@
  
          # Dynamically update privileges for those ace's that are inherited.
          if inheritance:
-@@ -1146,49 +1221,96 @@
+@@ -1070,7 +1145,7 @@
+                                 # Adjust ACE for inherit on this resource
+                                 children = list(ace.children)
+                                 children.remove(TwistedACLInheritable())
+-                                children.append(davxml.Inherited(davxml.HRef.fromString(parentURL)))
++                                children.append(davxml.Inherited(davxml.HRef(parentURL)))
+                                 aces.append(davxml.ACE(*children))
+             else:
+                 aces.extend(inherited_aces)
+@@ -1122,7 +1197,7 @@
+                 # 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
+@@ -1146,49 +1221,69 @@
  
          This implementation returns an empty set.
          """
@@ -378,78 +452,58 @@
              It will errback with an HTTPError(responsecode.FORBIDDEN) if
              the principal isn't found.
          """
-+        def gotAuthn(authnPrincipal):
-+            if authnPrincipal is None:
-+                log.msg("Could not find the principal resource for user id: %s" % (authid,))
-+                raise HTTPError(responsecode.FORBIDDEN)
-+
-+            def gotAuthz(authzPrincipal):
-+                return (authnPrincipal, authzPrincipal)
-+
-+            d = self.authorizationPrincipal(request, authid, authnPrincipal)
-+            d.addCallback(gotAuthz)
-+            return d
-+
-+        d = self.findPrincipalForAuthID(request, authid)
-+        d.addCallback(gotAuthn)
-+        return d
-+
-+    def findPrincipalForAuthID(self, request, authid):
-+        """
-+        Return authentication and authoirization prinicipal identifiers for the
-+        authentication identifer passed in. In this implementation authn and authz
-+        principals are the same.
-+
-+        @param request: the L{IRequest} for the request in progress.
-+        @param authid: a string containing the
-+            authentication/authorization identifier for the principal
-+            to lookup.
-+        @return: a tuple of C{(principal, principalURI)} where: C{principal} is the L{Principal}
-+            that is found; {principalURI} is the C{str} URI of the principal.
-+            If not found return None.
-+        """
-+        # FIXME: should self.principalCollections() return resources instead of URIs?
-+
-         # Try to match principals in each principal collection on the resource
-         collections = waitForDeferred(self.principalCollections(request))
-         yield collections
-         collections = collections.getResult()
+-        # Try to match principals in each principal collection on the resource
+-        collections = waitForDeferred(self.principalCollections(request))
+-        yield collections
+-        collections = collections.getResult()
++        authnPrincipal = self.findPrincipalForAuthID(authid)
  
 -        for collection in collections:
 -            principalURI = joinURL(str(collection), authid)
-+        for collectionURI in collections:
-+            collection = waitForDeferred(request.locateResource(collectionURI))
-+            yield collection
-+            collection = collection.getResult()
++        if authnPrincipal is None:
++            log.msg("Could not find the principal resource for user id: %s" % (authid,))
++            raise HTTPError(responsecode.FORBIDDEN)
  
 -            principal = waitForDeferred(request.locateResource(principalURI))
 -            yield principal
 -            principal = principal.getResult()
-+            assert collection is not None, "Unable to locate principal collection %s" % (collectionURI,)
++        d = self.authorizationPrincipal(request, authid, authnPrincipal)
++        d.addCallback(lambda authzPrincipal: (authnPrincipal, authzPrincipal))
++        return d
  
 -            if isPrincipalResource(principal):
 -                yield (principal, principalURI)
-+            # FIXME: collection = IPrincipalCollectionResource(collection)
-+            principal = collection.principalForUser(authid)
-+
-+            if principal:
-+                yield principal
-                 return
-         else:
+-                return
+-        else:
 -            principalCollections = waitForDeferred(self.principalCollections(request))
 -            yield principalCollections
 -            principalCollections = principalCollections.getResult()
-+            yield None
-+            return
++    def findPrincipalForAuthID(self, authid):
++        """
++        Return authentication and authoirization prinicipal identifiers for the
++        authentication identifer passed in. In this implementation authn and authz
++        principals are the same.
  
 -            if len(principalCollections) == 0:
 -                log.msg("DAV:principal-collection-set property cannot be found on the resource being authorized: %s" % self)
 -            else:
 -                log.msg("Could not find principal matching user id: %s" % authid)
 -            raise HTTPError(responsecode.FORBIDDEN)
++        @param authid: a string containing the
++            authentication/authorization identifier for the principal
++            to lookup.
++        @return: a tuple of C{(principal, principalURI)} where: C{principal} is the L{Principal}
++            that is found; {principalURI} is the C{str} URI of the principal.
++            If not found return None.
++        """
++        for collection in self.principalCollections():
++            principal = collection.principalForUser(authid)
++            if principal is not None:
++                return principal
++        return None
+ 
+-    findPrincipalForAuthID = deferredGenerator(findPrincipalForAuthID)
 -
-     findPrincipalForAuthID = deferredGenerator(findPrincipalForAuthID)
- 
 +    def authorizationPrincipal(self, request, authid, authnPrincipal):
 +        """
 +        Determine the authorization principal for the given request and authentication principal.
@@ -467,7 +521,7 @@
      def samePrincipal(self, principal1, principal2):
          """
          Check whether the two prinicpals are exactly the same in terms of
-@@ -1213,7 +1335,6 @@
+@@ -1213,7 +1308,6 @@
              return False
                  
      def matchPrincipal(self, principal1, principal2, request):
@@ -475,7 +529,7 @@
          """
          Check whether the principal1 is a principal in the set defined by
          principal2.
-@@ -1238,6 +1359,9 @@
+@@ -1238,6 +1332,9 @@
              if isinstance(principal1, davxml.Unauthenticated):
                  yield False
                  return
@@ -485,7 +539,7 @@
              else:
                  yield True
                  return
-@@ -1265,7 +1389,6 @@
+@@ -1265,7 +1362,6 @@
  
          assert principal2 is not None, "principal2 is None"
  
@@ -493,7 +547,16 @@
          # Compare two HRefs and do group membership test as well
          if principal1 == principal2:
              yield True
-@@ -1511,6 +1634,265 @@
+@@ -1426,7 +1522,7 @@
+                 log.err("DAV:self ACE is set on non-principal resource %r" % (self,))
+                 yield None
+                 return
+-            principal = davxml.HRef.fromString(self.principalURL())
++            principal = davxml.HRef(self.principalURL())
+ 
+         if isinstance(principal, davxml.HRef):
+             yield principal
+@@ -1511,6 +1607,265 @@
          return None
  
      ##
@@ -639,7 +702,7 @@
 +        assert maxsize is None or isinstance(maxsize, int), "maxsize must be an int or None"
 +        
 +        if maxsize is not None:
-+            self.writeDeadProperty(TwistedQuotaRootProperty.fromString(str(maxsize)))
++            self.writeDeadProperty(TwistedQuotaRootProperty(str(maxsize)))
 +        else:
 +            # Remove both the root and the cached used value
 +            self.removeDeadProperty(TwistedQuotaRootProperty)
@@ -729,7 +792,7 @@
 +        else:
 +            # Do brute force size determination and cache the result in the private property
 +            def _defer(result):
-+                self.writeDeadProperty(TwistedQuotaUsedProperty.fromString(str(result)))
++                self.writeDeadProperty(TwistedQuotaUsedProperty(str(result)))
 +                return result
 +            d = self.quotaSize(request)
 +            d.addCallback(_defer)
@@ -749,7 +812,7 @@
 +        # Get current value
 +        def _defer(size):
 +            size += adjust
-+            self.writeDeadProperty(TwistedQuotaUsedProperty.fromString(str(size)))
++            self.writeDeadProperty(TwistedQuotaUsedProperty(str(size)))
 +
 +        d = self.currentQuotaUse(request)
 +        d.addCallback(_defer)
@@ -759,7 +822,7 @@
      # HTTP
      ##
  
-@@ -1558,7 +1940,7 @@
+@@ -1558,7 +1913,7 @@
      """
      DAV resource with no children.
      """
@@ -768,7 +831,32 @@
          return succeed(None)
  
  class DAVPrincipalResource (DAVLeafResource):
-@@ -1712,6 +2094,37 @@
+@@ -1673,6 +2028,24 @@
+         else:
+             return uri in self.groupMembers()
+ 
++class DAVPrincipalCollectionResource (DAVResource):
++    """
++    WebDAV principal collection resource.  (RFC 3744, section 5.8)
++    """
++    implements(IDAVPrincipalCollectionResource)
++
++    def __init__(self, url, principalCollections=()):
++        """
++        @param url: This resource's URL.
++        """
++        DAVResource.__init__(self, principalCollections=principalCollections)
++
++        assert url.endswith("/"), "Collection URL must end in '/'"
++        self._url = url
++
++    def principalCollectionURL(self):
++        return self._url
++
+ class AccessDeniedError(Exception):
+     def __init__(self, errors):
+         """ 
+@@ -1712,6 +2085,37 @@
  davxml.registerElement(TwistedACLInheritable)
  davxml.ACE.allowed_children[(twisted_dav_namespace, "inheritable")] = (0, 1)
  

Modified: CalendarServer/branches/users/wsanchez/provisioning-2/lib-patches/Twisted/twisted.web2.dav.static.patch
===================================================================
--- CalendarServer/branches/users/wsanchez/provisioning-2/lib-patches/Twisted/twisted.web2.dav.static.patch	2006-11-29 03:07:23 UTC (rev 622)
+++ CalendarServer/branches/users/wsanchez/provisioning-2/lib-patches/Twisted/twisted.web2.dav.static.patch	2006-11-30 23:08:20 UTC (rev 623)
@@ -8,10 +8,10 @@
  
 -import os
 -
--from twisted.python import log
- from twisted.internet.defer import succeed, deferredGenerator, waitForDeferred
++from twisted.internet.defer import succeed, deferredGenerator, waitForDeferred
+ from twisted.python import log
+-from twisted.internet.defer import succeed, deferredGenerator, waitForDeferred
 -from twisted.web2.static import File
-+from twisted.python import log
 +from twisted.web2 import http_headers
  from twisted.web2 import responsecode, dirlist
 -from twisted.web2.http import RedirectResponse
@@ -24,7 +24,42 @@
  
  try:
      from twisted.web2.dav.xattrprops import xattrPropertyStore as DeadPropertyStore
-@@ -75,6 +75,12 @@
+@@ -52,9 +52,11 @@
+ 
+     Extends twisted.web2.static.File to handle WebDAV methods.
+     """
+-    def __init__(self, path,
+-                 defaultType="text/plain",
+-                 indexNames=None):
++    def __init__(
++        self, path,
++        defaultType="text/plain", indexNames=None,
++        principalCollections=()
++    ):
+         """
+         @param path: the path of the file backing this resource.
+         @param defaultType: the default mime type (as a string) for this
+@@ -62,11 +64,14 @@
+         @param indexNames: a sequence of index file names.
+         @param acl: an L{IDAVAccessControlList} with the .
+         """
+-        super(DAVFile, self).__init__(path,
+-                                      defaultType = defaultType,
+-                                      ignoredExts = (),
+-                                      processors  = None,
+-                                      indexNames  = indexNames)
++        File.__init__(
++            self, path,
++            defaultType = defaultType,
++            ignoredExts = (),
++            processors = None,
++            indexNames = indexNames,
++        )
++        DAVResource.__init__(self, principalCollections=principalCollections)
+ 
+     def __repr__(self):
+         return "<%s: %s>" % (self.__class__.__name__, self.fp.path)
+@@ -75,6 +80,12 @@
      # WebDAV
      ##
  
@@ -37,7 +72,7 @@
      def davComplianceClasses(self):
          return ("1", "access-control") # Add "2" when we have locking
  
-@@ -87,7 +93,6 @@
+@@ -87,7 +98,6 @@
          """
          See L{IDAVResource.isCollection}.
          """
@@ -45,7 +80,7 @@
          return self.fp.isdir()
  
      ##
-@@ -98,6 +103,50 @@
+@@ -98,6 +108,50 @@
          return succeed(davPrivilegeSet)
  
      ##
@@ -96,9 +131,15 @@
      # Workarounds for issues with File
      ##
  
-@@ -134,61 +183,6 @@
+@@ -132,63 +186,11 @@
+         return (self.createSimilarFile(self.fp.child(path).path), segments[1:])
+ 
      def createSimilarFile(self, path):
-         return self.__class__(path, defaultType=self.defaultType, indexNames=self.indexNames[:])
+-        return self.__class__(path, defaultType=self.defaultType, indexNames=self.indexNames[:])
++        return self.__class__(
++            path, self.defaultType, self.indexNames[:],
++            principalCollections=self.principalCollections()
++        )
  
 -    def render(self, request):
 -        """

Modified: CalendarServer/branches/users/wsanchez/provisioning-2/lib-patches/Twisted/twisted.web2.dav.test.test_resource.patch
===================================================================
--- CalendarServer/branches/users/wsanchez/provisioning-2/lib-patches/Twisted/twisted.web2.dav.test.test_resource.patch	2006-11-29 03:07:23 UTC (rev 622)
+++ CalendarServer/branches/users/wsanchez/provisioning-2/lib-patches/Twisted/twisted.web2.dav.test.test_resource.patch	2006-11-30 23:08:20 UTC (rev 623)
@@ -2,7 +2,23 @@
 ===================================================================
 --- twisted/web2/dav/test/test_resource.py	(revision 18545)
 +++ twisted/web2/dav/test/test_resource.py	(working copy)
-@@ -282,7 +282,8 @@
+@@ -192,13 +192,10 @@
+ class AccessTests(TestCase):
+     def setUp(self):
+         gooduser = TestDAVPrincipalResource('/users/gooduser')
++        gooduser.writeDeadProperty(TwistedPasswordProperty('goodpass'))
+ 
+-        gooduser.writeDeadProperty(
+-            TwistedPasswordProperty.fromString('goodpass'))
+-
+         baduser = TestDAVPrincipalResource('/users/baduser')
+-        baduser.writeDeadProperty(
+-            TwistedPasswordProperty.fromString('badpass'))
++        baduser.writeDeadProperty(TwistedPasswordProperty('badpass'))
+ 
+         protected = TestResource('/protected')
+         protected.setAccessControlList(davxml.ACL(
+@@ -282,7 +279,8 @@
          # Has auth; should allow
  
          request = SimpleRequest(site, "GET", "/")
@@ -12,7 +28,22 @@
          d = request.locateResource('/')
          d.addCallback(_checkPrivileges)
          d.addCallback(expectOK)
-@@ -380,8 +381,8 @@
+@@ -348,12 +346,12 @@
+             davxml.Grant(davxml.Privilege(davxml.All())),
+             davxml.Protected()))
+ 
+-    def __init__(self, uri=None, children=None):
++    def __init__(self, uri=None, children=None, principalCollections=()):
+         """
+         @param uri: A string respresenting the URI of the given resource
+         @param children: a dictionary of names to Resources
+         """
+-
++        DAVResource.__init__(self, principalCollections=principalCollections)
+         self.children = children
+         self.uri = uri
+ 
+@@ -380,8 +378,8 @@
          return succeed(davPrivilegeSet)
  
      def currentPrincipal(self, request):
@@ -23,7 +54,7 @@
          else:
              return davxml.Principal(davxml.Unauthenticated())
  
-@@ -400,6 +401,8 @@
+@@ -400,17 +398,23 @@
      def accessControlList(self, request, **kwargs):
          return succeed(self.acl)
      
@@ -31,11 +62,24 @@
 +        return self.children[user]
  
  class AuthAllResource (TestResource):
-     """Give Authenticated principals all privileges deny everything else
-@@ -414,3 +417,6 @@
+-    """Give Authenticated principals all privileges deny everything else
+     """
++    Give Authenticated principals all privileges and deny everyone else.
++    """
+     acl = davxml.ACL(
+         davxml.ACE(
+             davxml.Principal(davxml.Authenticated()),
+             davxml.Grant(davxml.Privilege(davxml.All())),
+-            davxml.Protected()))
+-
++            davxml.Protected()
++        )
++    )
+     
  class TestDAVPrincipalResource(DAVPrincipalResource, TestResource):
-     """Get deadProperties from TestResource
-     """
+-    """Get deadProperties from TestResource
+-    """
++    # Get dead properties from TestResource
 +
 +    def principalURL(self):
 +        return self.uri

Modified: CalendarServer/branches/users/wsanchez/provisioning-2/support/CalendarServer.tmproj
===================================================================
--- CalendarServer/branches/users/wsanchez/provisioning-2/support/CalendarServer.tmproj	2006-11-29 03:07:23 UTC (rev 622)
+++ CalendarServer/branches/users/wsanchez/provisioning-2/support/CalendarServer.tmproj	2006-11-30 23:08:20 UTC (rev 623)
@@ -2,6 +2,8 @@
 <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
 <plist version="1.0">
 <dict>
+	<key>currentDocument</key>
+	<string>../../Twisted/twisted/web2/dav/resource.py</string>
 	<key>documents</key>
 	<array>
 		<dict>
@@ -15,6 +17,8 @@
 			<string>../twistedcaldav</string>
 		</dict>
 		<dict>
+			<key>expanded</key>
+			<true/>
 			<key>name</key>
 			<string>web2</string>
 			<key>regexFolderFilter</key>
@@ -123,10 +127,60 @@
 	<key>fileHierarchyDrawerWidth</key>
 	<integer>325</integer>
 	<key>metaData</key>
-	<dict/>
+	<dict>
+		<key>../../Twisted/twisted/web2/dav/resource.py</key>
+		<dict>
+			<key>caret</key>
+			<dict>
+				<key>column</key>
+				<integer>56</integer>
+				<key>line</key>
+				<integer>1265</integer>
+			</dict>
+			<key>firstVisibleColumn</key>
+			<integer>0</integer>
+			<key>firstVisibleLine</key>
+			<integer>1246</integer>
+		</dict>
+		<key>../twistedcaldav/resource.py</key>
+		<dict>
+			<key>caret</key>
+			<dict>
+				<key>column</key>
+				<integer>80</integer>
+				<key>line</key>
+				<integer>261</integer>
+			</dict>
+			<key>columnSelection</key>
+			<false/>
+			<key>firstVisibleColumn</key>
+			<integer>0</integer>
+			<key>firstVisibleLine</key>
+			<integer>231</integer>
+			<key>selectFrom</key>
+			<dict>
+				<key>column</key>
+				<integer>58</integer>
+				<key>line</key>
+				<integer>261</integer>
+			</dict>
+			<key>selectTo</key>
+			<dict>
+				<key>column</key>
+				<integer>80</integer>
+				<key>line</key>
+				<integer>261</integer>
+			</dict>
+		</dict>
+	</dict>
+	<key>openDocuments</key>
+	<array>
+		<string>../../Twisted/twisted/web2/dav/resource.py</string>
+		<string>../twistedcaldav/resource.py</string>
+	</array>
 	<key>showFileHierarchyDrawer</key>
 	<true/>
 	<key>windowFrame</key>
-	<string>{{591, 84}, {1019, 1050}}</string>
+	<string>{{705, 64}, {1019, 1050}}</string>
 </dict>
 </plist>

Modified: CalendarServer/branches/users/wsanchez/provisioning-2/support/patchmaker.py
===================================================================
--- CalendarServer/branches/users/wsanchez/provisioning-2/support/patchmaker.py	2006-11-29 03:07:23 UTC (rev 622)
+++ CalendarServer/branches/users/wsanchez/provisioning-2/support/patchmaker.py	2006-11-30 23:08:20 UTC (rev 623)
@@ -30,7 +30,7 @@
 cwd = os.getcwd()
 libpatches = os.path.join(cwd, "lib-patches")
 
-svn = "/usr/local/subversion/bin/svn"
+svn = "/usr/bin/svn"
 
 # Stuff we have to manually ignore because our ignore logic cannot cope
 ignores = set((

Modified: CalendarServer/branches/users/wsanchez/provisioning-2/twistedcaldav/directory/principal.py
===================================================================
--- CalendarServer/branches/users/wsanchez/provisioning-2/twistedcaldav/directory/principal.py	2006-11-29 03:07:23 UTC (rev 622)
+++ CalendarServer/branches/users/wsanchez/provisioning-2/twistedcaldav/directory/principal.py	2006-11-30 23:08:20 UTC (rev 623)
@@ -115,8 +115,8 @@
     # ACL
     ##
 
-    def principalCollections(self, request):
-        return succeed((self.principalCollectionURL(),))
+    def principalCollections(self):
+        return (self,)
 
 class DirectoryPrincipalTypeResource (PermissionsMixIn, CalendarPrincipalCollectionResource, DAVFile):
     """
@@ -178,8 +178,8 @@
     # ACL
     ##
 
-    def principalCollections(self, request):
-        return self._parent.principalCollections(request)
+    def principalCollections(self):
+        return self._parent.principalCollections()
 
 class DirectoryPrincipalResource (PermissionsMixIn, CalendarPrincipalFile):
     """
@@ -284,8 +284,8 @@
     def groupMemberships(self):
         return self._getRelatives("groups")
 
-    def principalCollections(self, request):
-        return self._parent.principalCollections(request)
+    def principalCollections(self):
+        return self._parent.principalCollections()
 
     ##
     # CalDAV

Modified: CalendarServer/branches/users/wsanchez/provisioning-2/twistedcaldav/directory/test/test_principal.py
===================================================================
--- CalendarServer/branches/users/wsanchez/provisioning-2/twistedcaldav/directory/test/test_principal.py	2006-11-29 03:07:23 UTC (rev 622)
+++ CalendarServer/branches/users/wsanchez/provisioning-2/twistedcaldav/directory/test/test_principal.py	2006-11-30 23:08:20 UTC (rev 623)
@@ -73,7 +73,6 @@
 
             self.principalRootResources[directory.__class__.__name__] = provisioningResource
 
-    @deferredGenerator
     def test_hierarchy(self):
         """
         DirectoryPrincipalProvisioningResource.listChildren(),
@@ -95,10 +94,8 @@
             provisioningURL = "/" + directory.__class__.__name__ + "/"
             self.assertEquals(provisioningURL, provisioningResource.principalCollectionURL())
 
-            principalCollections = waitForDeferred(provisioningResource.principalCollections(None))
-            yield principalCollections
-            principalCollections = principalCollections.getResult()
-            self.assertEquals(set((provisioningURL,)), set(principalCollections))
+            principalCollections = provisioningResource.principalCollections()
+            self.assertEquals(set((provisioningURL,)), set(pc.principalCollectionURL() for pc in principalCollections))
 
             recordTypes = set(provisioningResource.listChildren())
             self.assertEquals(recordTypes, set(directory.recordTypes()))
@@ -111,10 +108,8 @@
                 typeURL = provisioningURL + recordType + "/"
                 self.assertEquals(typeURL, typeResource.principalCollectionURL())
 
-                principalCollections = waitForDeferred(typeResource.principalCollections(None))
-                yield principalCollections
-                principalCollections = principalCollections.getResult()
-                self.assertEquals(set((provisioningURL,)), set(principalCollections))
+                principalCollections = typeResource.principalCollections()
+                self.assertEquals(set((provisioningURL,)), set(pc.principalCollectionURL() for pc in principalCollections))
 
                 shortNames = set(typeResource.listChildren())
                 self.assertEquals(shortNames, set(r.shortName for r in directory.listRecords(recordType)))
@@ -127,10 +122,8 @@
                     recordURL = typeURL + shortName
                     self.assertEquals(recordURL, recordResource.principalURL())
 
-                    principalCollections = waitForDeferred(recordResource.principalCollections(None))
-                    yield principalCollections
-                    principalCollections = principalCollections.getResult()
-                    self.assertEquals(set((provisioningURL,)), set(principalCollections))
+                    principalCollections = recordResource.principalCollections()
+                    self.assertEquals(set((provisioningURL,)), set(pc.principalCollectionURL() for pc in principalCollections))
 
     def test_principalForUser(self):
         """

Modified: CalendarServer/branches/users/wsanchez/provisioning-2/twistedcaldav/resource.py
===================================================================
--- CalendarServer/branches/users/wsanchez/provisioning-2/twistedcaldav/resource.py	2006-11-29 03:07:23 UTC (rev 622)
+++ CalendarServer/branches/users/wsanchez/provisioning-2/twistedcaldav/resource.py	2006-11-30 23:08:20 UTC (rev 623)
@@ -33,7 +33,6 @@
     "isScheduleOutboxResource",
 ]
 
-from weakref import WeakValueDictionary
 from zope.interface import implements
 
 from twisted.internet import reactor
@@ -42,7 +41,8 @@
 from twisted.python import log
 from twisted.web2 import responsecode
 from twisted.web2.dav import davxml
-from twisted.web2.dav.resource import AccessDeniedError, DAVPrincipalResource
+from twisted.web2.dav.idav import IDAVPrincipalCollectionResource
+from twisted.web2.dav.resource import AccessDeniedError, DAVPrincipalResource, DAVPrincipalCollectionResource
 from twisted.web2.dav.davxml import dav_namespace
 from twisted.web2.dav.http import ErrorResponse
 from twisted.web2.dav.resource import TwistedACLInheritable
@@ -285,15 +285,6 @@
 
     authorizationPrincipal = deferredGenerator(authorizationPrincipal)
 
-    def principalCollections(self, request):
-        # Get the values cached in CalendarPrincipalCollectionResource.
-        collections = CalendarPrincipalCollectionResource.principleCollectionSet.keys()
-        if collections:
-            return succeed(collections)
-
-        # Fall back to super's implementation.
-        return super(CalDAVResource, self).principalCollections(request)
-
     ##
     # CalDAV
     ##
@@ -497,13 +488,11 @@
         """
         return request.locateResource(parentForURL(uri))
 
-class CalendarPrincipalCollectionResource (CalDAVResource):
+class CalendarPrincipalCollectionResource (DAVPrincipalCollectionResource, CalDAVResource):
     """
     CalDAV principal collection.
     """
-    # Use a WeakKeyDictionary to keep track of all instances.
-    # A WeakKeySet would be more appropriate, but there is no such class yet.
-    principleCollectionSet = WeakValueDictionary()
+    implements(IDAVPrincipalCollectionResource)
 
     @classmethod
     def outboxForCalendarUser(clazz, request, address):
@@ -545,18 +534,6 @@
         d.addCallback(_defer)
         return d
 
-    def __init__(self, url):
-        assert url.endswith("/"), "Collection URL must end in '/'"
-
-        # FIXME: there is no super implementation of __init__
-        #super(CalendarPrincipalCollectionResource, self).__init__()
-
-        self._url = url
-
-        # Register self with class
-        if url not in CalendarPrincipalCollectionResource.principleCollectionSet:
-            CalendarPrincipalCollectionResource.principleCollectionSet[url] = self
-
     def isCollection(self):
         return True
 
@@ -591,9 +568,6 @@
 
     findCalendarUser = deferredGenerator(findCalendarUser)
 
-    def principalCollectionURL(self):
-        return self._url
-
     def supportedReports(self):
         """
         Principal collections are the only resources supporting the
@@ -625,6 +599,7 @@
             ),
         )
 
+# FIXME: Replace this
 def findAnyCalendarUser(request, address):
     """
     Find the calendar user principal associated with the specified calendar
@@ -634,26 +609,18 @@
     @return: the L{CalendarPrincipalResource} for the specified calendar
         user, or C{None} if the user is not found.
     """
-    for url in CalendarPrincipalCollectionResource.principleCollectionSet.keys():
-        try:
-            # Explicitly locate the prinicpal collection resource to force URL caching in request
-            collection = waitForDeferred(request.locateResource(url))
-            yield collection
-            collection = collection.getResult()
+    for collection in self.principalCollections():
+        if isinstance(collection, CalendarPrincipalCollectionResource):
+            principal = waitForDeferred(collection.findCalendarUser(request, address))
+            yield principal
+            principal = principal.getResult()
 
-            if isinstance(collection, CalendarPrincipalCollectionResource):
-                principal = waitForDeferred(collection.findCalendarUser(request, address))
+            if principal is not None:
                 yield principal
-                principal = principal.getResult()
+                return
+    else:
+        yield None
 
-                if principal is not None:
-                    yield principal
-                    return
-        except ReferenceError:
-            pass
-
-    yield None
-
 findAnyCalendarUser = deferredGenerator(findAnyCalendarUser)
 
 class CalendarPrincipalResource (DAVPrincipalResource):

Modified: CalendarServer/branches/users/wsanchez/provisioning-2/twistedcaldav/static.py
===================================================================
--- CalendarServer/branches/users/wsanchez/provisioning-2/twistedcaldav/static.py	2006-11-29 03:07:23 UTC (rev 622)
+++ CalendarServer/branches/users/wsanchez/provisioning-2/twistedcaldav/static.py	2006-11-30 23:08:20 UTC (rev 623)
@@ -496,10 +496,10 @@
     def listChildren(self):
         return self.directory.recordTypes()
 
-    def principalCollections(self, request):
+    def principalCollections(self):
         # FIXME: directory.principalCollection smells like a hack
         # See DirectoryPrincipalProvisioningResource.__init__()
-        return self.directory.principalCollection.principalCollections(request)
+        return self.directory.principalCollection.principalCollections()
 
     def homeForDirectoryRecord(self, record):
         return self.getChild(record.recordType).getChild(record.shortName)
@@ -572,8 +572,8 @@
     def defaultAccessControlList(self):
         return readOnlyACL
 
-    def principalCollections(self, request):
-        return self._parent.principalCollections(request)
+    def principalCollections(self):
+        return self._parent.principalCollections()
 
 class CalendarHomeFile (CalDAVFile):
     """

Modified: CalendarServer/branches/users/wsanchez/provisioning-2/twistedcaldav/test/test_mkcalendar.py
===================================================================
--- CalendarServer/branches/users/wsanchez/provisioning-2/twistedcaldav/test/test_mkcalendar.py	2006-11-29 03:07:23 UTC (rev 622)
+++ CalendarServer/branches/users/wsanchez/provisioning-2/twistedcaldav/test/test_mkcalendar.py	2006-11-30 23:08:20 UTC (rev 623)
@@ -122,10 +122,10 @@
         mk = caldavxml.MakeCalendar(
             davxml.Set(
                 davxml.PropertyContainer(
-                    davxml.DisplayName.fromString("Lisa's Events"),
-                    caldavxml.CalendarDescription.fromString("Calendar restricted to events."), # FIXME: lang=en
+                    davxml.DisplayName("Lisa's Events"),
+                    caldavxml.CalendarDescription("Calendar restricted to events."), # FIXME: lang=en
                     caldavxml.SupportedCalendarComponentSet(caldavxml.CalendarComponent(name="VEVENT")),
-                    caldavxml.CalendarTimeZone.fromString(
+                    caldavxml.CalendarTimeZone(
 """BEGIN:VCALENDAR
 PRODID:-//Example Corp.//CalDAV Client//EN
 VERSION:2.0

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20061130/b8c85bc8/attachment.html


More information about the calendarserver-changes mailing list