[CalendarServer-changes] [1810] CalendarServer/branches/release/CalendarServer-1.0-dev

source_changes at macosforge.org source_changes at macosforge.org
Mon Aug 20 19:22:47 PDT 2007


Revision: 1810
          http://trac.macosforge.org/projects/calendarserver/changeset/1810
Author:   wsanchez at apple.com
Date:     2007-08-20 19:22:46 -0700 (Mon, 20 Aug 2007)

Log Message:
-----------
Pulled up r1745, r1748, r1757, r1758

Modified Paths:
--------------
    CalendarServer/branches/release/CalendarServer-1.0-dev/conf/accounts-test.xml
    CalendarServer/branches/release/CalendarServer-1.0-dev/conf/caldavd-test.plist
    CalendarServer/branches/release/CalendarServer-1.0-dev/lib-patches/Twisted/twisted.web2.dav.method.report_principal_match.patch
    CalendarServer/branches/release/CalendarServer-1.0-dev/lib-patches/Twisted/twisted.web2.dav.resource.patch
    CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/calendaruserproxy.py
    CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/principal.py
    CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/test/test_principal.py
    CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/xmlaccountsparser.py
    CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/xmlfile.py
    CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/root.py
    CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/test/test_root.py

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/conf/accounts-test.xml
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/conf/accounts-test.xml	2007-08-21 00:35:50 UTC (rev 1809)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/conf/accounts-test.xml	2007-08-21 02:22:46 UTC (rev 1810)
@@ -21,29 +21,34 @@
 <accounts realm="Test Realm">
   <user>
     <uid>admin</uid>
+    <guid>admin</guid>
     <password>admin</password>
     <name>Super User</name>
   </user>
   <user repeat="99">
     <uid>user%02d</uid>
+    <guid>user%02d</guid>
     <password>user%02d</password>
     <name>User %02d</name>
     <cuaddr>mailto:user%02d at example.com</cuaddr>
   </user>
   <user repeat="10">
     <uid>public%02d</uid>
+    <guid>public%02d</guid>
     <password>public%02d</password>
     <name>Public %02d</name>
     <cuaddr>mailto:public%02d at example.com</cuaddr>
   </user>
   <location repeat="10">
     <uid>location%02d</uid>
+    <guid>location%02d</guid>
     <password>location%02d</password>
     <name>Room %02d</name>
     <auto-schedule/>
   </location>
   <resource repeat="10">
     <uid>resource%02d</uid>
+    <guid>resource%02d</guid>
     <password>resource%02d</password>
     <name>Resource %02d</name>
     <auto-schedule/>
@@ -53,6 +58,7 @@
   </resource>
   <group>
     <uid>group01</uid>
+    <guid>group01</guid>
     <password>group01</password>
     <name>Group 01</name>
     <members>

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/conf/caldavd-test.plist
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/conf/caldavd-test.plist	2007-08-21 00:35:50 UTC (rev 1809)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/conf/caldavd-test.plist	2007-08-21 02:22:46 UTC (rev 1810)
@@ -186,7 +186,7 @@
   <!-- Principals with "DAV:all" access (relative URLs) -->
   <key>AdminPrincipals</key>
   <array>
-    <string>/principals/users/admin/</string>
+    <string>/principals/__uids__/admin/</string>
   </array>
 
   <!-- Principals that can pose as other principals -->

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/lib-patches/Twisted/twisted.web2.dav.method.report_principal_match.patch
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/lib-patches/Twisted/twisted.web2.dav.method.report_principal_match.patch	2007-08-21 00:35:50 UTC (rev 1809)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/lib-patches/Twisted/twisted.web2.dav.method.report_principal_match.patch	2007-08-21 02:22:46 UTC (rev 1810)
@@ -2,7 +2,7 @@
 ===================================================================
 --- twisted/web2/dav/method/report_principal_match.py	(revision 19773)
 +++ twisted/web2/dav/method/report_principal_match.py	(working copy)
-@@ -89,40 +89,53 @@
+@@ -89,40 +89,61 @@
          responses = []
          matchcount = 0
  
@@ -25,6 +25,10 @@
  
 -            for child, uri in children:
 -                if isPrincipalResource(child) and child.principalMatch(selfPrincipal):
+-                    # Check size of results is within limit
+-                    matchcount += 1
+-                    if matchcount > max_number_of_matches:
+-                        raise NumberOfMatchesWithinLimits
 +            # Find the set of principals that represent "self".
 +            
 +            # First add "self"
@@ -39,26 +43,31 @@
 +            # Now add each principal found to the response provided the principal resource is a child of
 +            # the current resource.
 +            for principal in selfItems:
-+                if principal.principalURL().startswith(request.uri):
-                     # Check size of results is within limit
-                     matchcount += 1
-                     if matchcount > max_number_of_matches:
-                         raise NumberOfMatchesWithinLimits
--
-+    
-                     d = waitForDeferred(prop_common.responseForHref(
-                         request,
-                         responses,
--                        davxml.HRef.fromString(uri),
--                        child,
-+                        davxml.HRef.fromString(principal.principalURL()),
-+                        principal,
-                         propertiesForResource,
-                         propElement
-                     ))
-                     yield d
-                     d.getResult()
-         else:
++                # Get all the URIs that point to the principal resource
++                # FIXME: making the assumption that the principalURL() is the URL of the resource we found
++                principal_uris = [principal.principalURL()]
++                principal_uris.extend(principal.alternateURIs())
++                
++                # Compare each one to the request URI and return at most one that matches
++                for uri in principal_uris:
++                    if uri.startswith(request.uri):
++                        # Check size of results is within limit
++                        matchcount += 1
++                        if matchcount > max_number_of_matches:
++                            raise NumberOfMatchesWithinLimits
++        
++                        d = waitForDeferred(prop_common.responseForHref(
++                            request,
++                            responses,
++                            davxml.HRef.fromString(uri),
++                            principal,
++                            propertiesForResource,
++                            propElement
++                        ))
++                        yield d
++                        d.getResult()
++                        break
++        else:
 +            # 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 = waitForDeferred(self.inheritedACEsforChildren(request))
@@ -70,11 +79,22 @@
 +                                                  privileges=(davxml.Read(),), inherited_aces=filteredaces))
 +            yield d
 +            d.getResult()
-+
+ 
+-                    d = waitForDeferred(prop_common.responseForHref(
+-                        request,
+-                        responses,
+-                        davxml.HRef.fromString(uri),
+-                        child,
+-                        propertiesForResource,
+-                        propElement
+-                    ))
+-                    yield d
+-                    d.getResult()
+-        else:
              for child, uri in children:
                  # Try to read the requested property from this resource
                  try:
-@@ -137,7 +150,7 @@
+@@ -137,7 +158,7 @@
                          yield principal
                          principal = principal.getResult()
  

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/lib-patches/Twisted/twisted.web2.dav.resource.patch
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/lib-patches/Twisted/twisted.web2.dav.resource.patch	2007-08-21 00:35:50 UTC (rev 1809)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/lib-patches/Twisted/twisted.web2.dav.resource.patch	2007-08-21 02:22:46 UTC (rev 1810)
@@ -517,7 +517,27 @@
                  
              return False
  
-@@ -1432,7 +1523,7 @@
+@@ -1351,11 +1442,16 @@
+         @return C{True} if C{href_principal} is valid, C{False} otherwise.
+ 
+         This implementation tests for a href element that corresponds to
+-        a principal resource.
++        a principal resource and matches the principal-URL.
+         """
+-        # Must have the principal resource type
++
++        # Must have the principal resource type and must match the principal-URL
++        
++        def _matchPrincipalURL(resource):
++            return isPrincipalResource(resource) and resource.principalURL() == str(href_principal)
++
+         d = request.locateResource(str(href_principal))
+-        d.addCallback(isPrincipalResource)
++        d.addCallback(_matchPrincipalURL)
+         return d
+ 
+     def resolvePrincipal(self, principal, request):
+@@ -1432,7 +1528,7 @@
                  log.err("DAV:self ACE is set on non-principal resource %r" % (self,))
                  yield None
                  return
@@ -526,7 +546,7 @@
  
          if isinstance(principal, davxml.HRef):
              yield principal
-@@ -1517,6 +1608,270 @@
+@@ -1517,6 +1613,270 @@
          return None
  
      ##
@@ -797,7 +817,7 @@
      # HTTP
      ##
  
-@@ -1567,7 +1922,7 @@
+@@ -1567,7 +1927,7 @@
      def findChildren(self, depth, request, callback, privileges=None, inherited_aces=None):
          return succeed(None)
  
@@ -806,7 +826,7 @@
      """
      Resource representing a WebDAV principal.  (RFC 3744, section 2)
      """
-@@ -1577,7 +1932,7 @@
+@@ -1577,7 +1937,7 @@
      # WebDAV
      ##
  
@@ -815,7 +835,7 @@
          (dav_namespace, "alternate-URI-set"),
          (dav_namespace, "principal-URL"    ),
          (dav_namespace, "group-member-set" ),
-@@ -1585,14 +1940,11 @@
+@@ -1585,14 +1945,11 @@
      )
  
      def davComplianceClasses(self):
@@ -831,7 +851,7 @@
      def readProperty(self, property, request):
          def defer():
              if type(property) is tuple:
-@@ -1610,10 +1962,10 @@
+@@ -1610,10 +1967,10 @@
                      return davxml.PrincipalURL(davxml.HRef(self.principalURL()))
  
                  if name == "group-member-set":
@@ -844,7 +864,7 @@
  
                  if name == "resourcetype":
                      if self.isCollection():
-@@ -1677,8 +2029,27 @@
+@@ -1677,8 +2034,27 @@
          if self.principalURL() == uri:
              return True
          else:
@@ -873,7 +893,7 @@
  class AccessDeniedError(Exception):
      def __init__(self, errors):
          """ 
-@@ -1718,6 +2089,37 @@
+@@ -1718,6 +2094,37 @@
  davxml.registerElement(TwistedACLInheritable)
  davxml.ACE.allowed_children[(twisted_dav_namespace, "inheritable")] = (0, 1)
  

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/calendaruserproxy.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/calendaruserproxy.py	2007-08-21 00:35:50 UTC (rev 1809)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/calendaruserproxy.py	2007-08-21 02:22:46 UTC (rev 1810)
@@ -24,6 +24,8 @@
     "CalendarUserProxyPrincipalResource",
 ]
 
+from cgi import escape
+
 from twisted.internet.defer import succeed
 from twisted.python import log
 from twisted.python.failure import Failure
@@ -91,15 +93,20 @@
         @param parent: the parent of this resource.
         @param proxyType: a C{str} containing the name of the resource.
         """
-        super(CalendarUserProxyPrincipalResource, self).__init__(path, joinURL(parent.principalURL(), proxyType))
-
-        self.parent = parent
-        self.pcollection = self.parent.parent.parent
-        self.proxyType = proxyType
-        self._url = joinURL(parent.principalURL(), proxyType)
         if self.isCollection():
-            self._url += "/"
+            slash = "/"
+        else:
+            slash = ""
 
+        url = joinURL(parent.principalURL(), proxyType) + slash
+
+        super(CalendarUserProxyPrincipalResource, self).__init__(path, url)
+
+        self.parent      = parent
+        self.proxyType   = proxyType
+        self.pcollection = self.parent.parent.parent # FIXME: if this is supposed to be public, it needs a better name
+        self._url        = url
+
         # Not terribly useful at present because we don't have a way
         # to map a GUID back to the correct principal.
         #self.guid = uuidFromName(self.parent.principalUID(), proxyType)
@@ -108,10 +115,19 @@
         # can easily map back to a principal.
         self.uid = "%s#%s" % (self.parent.principalUID(), proxyType)
 
+        self._alternate_urls = tuple(
+            joinURL(url, proxyType) + slash
+            for url in parent.alternateURIs()
+            if url.startswith("/")
+        )
+
         # Provision in __init__() because principals are used prior to request
         # lookups.
         self.provision()
 
+    def __str__(self):
+        return "%s [%s]" % (self.parent, self.proxyType)
+
     def _index(self):
         """
         Return the SQL database for this group principal.
@@ -169,7 +185,7 @@
         for uri in members:
             principal = self.pcollection._principalForURI(uri)
             # Invalid principals MUST result in an error.
-            if principal is None:
+            if principal is None or principal.principalURL() != uri:
                 raise HTTPError(StatusResponse(
                     responsecode.BAD_REQUEST,
                     "Attempt to use a non-existent principal %s as a group member of %s." % (uri, self.principalURL(),)
@@ -187,6 +203,8 @@
     ##
 
     def renderDirectoryBody(self, request):
+        # FIXME: Too much code duplication here from principal.py
+
         def format_list(items, *args):
             def genlist():
                 try:
@@ -204,6 +222,12 @@
         def link(url):
             return """<a href="%s">%s</a>""" % (url, url)
 
+        def format_principals(principals):
+            return format_list(
+                """<a href="%s">%s</a>""" % (principal.principalURL(), escape(str(principal)))
+                for principal in principals
+            )
+
         def gotSuper(output):
             return "".join((
                 """<div class="directory-listing">"""
@@ -228,9 +252,10 @@
                #"""GUID: %s\n"""                   % (self.guid,),
                 """Principal UID: %s\n"""          % (self.principalUID(),),
                 """Principal URL: %s\n"""          % (link(self.principalURL()),),
-                """\nAlternate URIs:\n"""          , format_list(self.alternateURIs()),
-                """\nGroup members (%s):\n"""      % ({False:"Locked", True:"Editable"}[self.hasEditableMembership()]), format_list(link(p.principalURL()) for p in self.groupMembers()),
-                """\nGroup memberships:\n"""       , format_list(link(p.principalURL()) for p in self.groupMemberships()),
+                """\nAlternate URIs:\n"""          , format_list(link(u) for u in self.alternateURIs()),
+                """\nGroup members (%s):\n""" % ({False:"Locked", True:"Editable"}[self.hasEditableMembership()])
+                                                   , format_principals(self.groupMembers()),
+                """\nGroup memberships:\n"""       , format_principals(self.groupMemberships()),
                 """</pre></blockquote></div>""",
                 output
             ))
@@ -252,7 +277,7 @@
 
     def alternateURIs(self):
         # FIXME: Add API to IDirectoryRecord for getting a record URI?
-        return ()
+        return self._alternate_urls
 
     def principalURL(self):
         return self._url

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/principal.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/principal.py	2007-08-21 00:35:50 UTC (rev 1809)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/principal.py	2007-08-21 02:22:46 UTC (rev 1810)
@@ -22,10 +22,11 @@
 
 __all__ = [
     "DirectoryPrincipalProvisioningResource",
-    "DirectoryPrincipalTypeResource",
+    "DirectoryPrincipalTypeProvisioningResource",
     "DirectoryPrincipalResource",
 ]
 
+from cgi import escape
 from urllib import unquote
 from urlparse import urlparse
 
@@ -46,6 +47,9 @@
 from twistedcaldav.static import AutoProvisioningFileMixIn
 from twistedcaldav.directory.idirectory import IDirectoryService
 
+# Use __underbars__ convention to avoid conflicts with directory resource types.
+uidsResourceName = "__uids__"
+
 # FIXME: These should not be tied to DAVFile
 # The reason that they is that web2.dav only implements DAV methods on
 # DAVFile instead of DAVResource.  That should change.
@@ -64,30 +68,35 @@
     CalendarPrincipalCollectionResource,
     DAVFile,
 ):
-    def principalForShortName(self, type, name):
-        raise NotImplementedError("Subclass must implement principalForShortName()")
+    def __init__(self, path, url, directory):
+        """
+        @param path: the path to the file which will back the resource.
+        @param url: the canonical URL for the resource.
+        @param directory: an L{IDirectoryService} to provision principals from.
+        """
+        assert url.endswith("/"), "Collection URL must end in '/'"
 
+        CalendarPrincipalCollectionResource.__init__(self, url)
+        DAVFile.__init__(self, path)
+
+        self.directory = IDirectoryService(directory)
+
+    def principalForShortName(self, recordType, name):
+        return self.principalForRecord(self.directory.recordWithShortName(recordType, name))
+
     def principalForUser(self, user):
         return self.principalForShortName(DirectoryService.recordType_users, user)
 
     def principalForGUID(self, guid):
-        record = self.directory.recordWithGUID(guid)
-        if record:
-            return self.principalForRecord(record)
-        else:
-            return None
+        return self.principalForUID(guid)
 
     def principalForUID(self, uid):
-        if "#" in uid:
-            # This UID belongs to a sub-principal
-            parent_uid, subType = uid.split("#")
-            return self.principalForGUID(parent_uid).getChild(subType)
-        else:
-            # This UID belongs to a primary principal (UID == GUID)
-            return self.principalForGUID(uid)
+        raise NotImplementedError("Subclass must implement principalForUID()")
 
     def principalForRecord(self, record):
-        return self.principalForShortName(record.recordType, record.shortName)
+        if record is None:
+            return None
+        return self.principalForGUID(record.guid)
 
     def principalForCalendarUserAddress(self, address):
         raise NotImplementedError("Subclass must implement principalForCalendarUserAddress()")
@@ -97,31 +106,22 @@
     Collection resource which provisions directory principals as its children.
     """
     def __init__(self, path, url, directory):
-        """
-        @param path: the path to the file which will back the resource.
-        @param url: the canonical URL for the resource.
-        @param directory: an L{IDirectoryService} to provision principals from.
-        """
-        assert url.endswith("/"), "Collection URL must end in '/'"
+        DirectoryProvisioningResource.__init__(self, path, url, directory)
 
-        CalendarPrincipalCollectionResource.__init__(self, url)
-        DAVFile.__init__(self, path)
-
-        self.directory = IDirectoryService(directory)
-
         # FIXME: Smells like a hack
         self.directory.principalCollection = self
 
+        #
         # Create children
+        #
         for recordType in self.directory.recordTypes():
-            self.putChild(recordType, DirectoryPrincipalTypeResource(self.fp.child(recordType).path, self, recordType))
+            self.putChild(recordType, DirectoryPrincipalTypeProvisioningResource(self, recordType))
 
-    def principalForShortName(self, type, name):
-        typeResource = self.getChild(type)
-        if typeResource is None:
-            return None
-        return typeResource.getChild(name)
+        self.putChild(uidsResourceName, DirectoryPrincipalUIDProvisioningResource(self))
 
+    def principalForUID(self, uid):
+        return self.getChild(uidsResourceName).getChild(uid)
+
     def _principalForURI(self, uri):
         scheme, netloc, path, params, query, fragment = urlparse(uri)
 
@@ -196,10 +196,13 @@
 
     def getChild(self, name):
         self.provision()
-        return self.putChildren.get(name, None)
+        if name == "":
+            return self
+        else:
+            return self.putChildren.get(name, None)
 
     def listChildren(self):
-        return self.putChildren.keys()
+        return self.directory.recordTypes()
 
     ##
     # ACL
@@ -208,25 +211,29 @@
     def principalCollections(self):
         return (self,)
 
-class DirectoryPrincipalTypeResource (DirectoryProvisioningResource):
+class DirectoryPrincipalTypeProvisioningResource (DirectoryProvisioningResource):
     """
-    Collection resource which provisions directory principals of a specific type as its children.
+    Collection resource which provisions directory principals of a
+    specific type as its children, indexed by short name.
     """
-    def __init__(self, path, parent, recordType):
+    def __init__(self, parent, recordType):
         """
         @param path: the path to the file which will back the resource.
-        @param directory: an L{IDirectoryService} to provision calendars from.
+        @param parent: the parent L{DirectoryPrincipalProvisioningResource}.
         @param recordType: the directory record type to provision.
         """
-        CalendarPrincipalCollectionResource.__init__(self, joinURL(parent.principalCollectionURL(), recordType) + "/")
-        DAVFile.__init__(self, path)
+        DirectoryProvisioningResource.__init__(
+            self,
+            parent.fp.child(recordType).path,
+            joinURL(parent.principalCollectionURL(), recordType) + "/",
+            parent.directory
+        )
 
-        self.directory = parent.directory
         self.recordType = recordType
         self.parent = parent
 
-    def principalForShortName(self, type, name):
-        return self.parent.principalForShortName(type, name)
+    def principalForUID(self, uid):
+        return self.parent.principalForUID(uid)
 
     def principalForCalendarUserAddress(self, address):
         return self.parent.principalForCalendarUserAddress(address)
@@ -239,24 +246,86 @@
         log.err("Attempt to create clone %r of resource %r" % (path, self))
         raise HTTPError(responsecode.NOT_FOUND)
 
-    def getChild(self, name, record=None):
+    def getChild(self, name):
         self.provision()
         if name == "":
             return self
+        else:
+            return self.principalForShortName(self.recordType, name)
 
+    def listChildren(self):
+        return (record.shortName for record in self.directory.listRecords(self.recordType))
+
+    ##
+    # ACL
+    ##
+
+    def principalCollections(self):
+        return self.parent.principalCollections()
+
+class DirectoryPrincipalUIDProvisioningResource (DirectoryProvisioningResource):
+    """
+    Collection resource which provisions directory principals indexed
+    by UID.
+    """
+    # FIXME: Remove path argument
+    def __init__(self, parent):
+        """
+        @param path: the path to the file which will back the resource.
+        @param directory: an L{IDirectoryService} to provision calendars from.
+        @param recordType: the directory record type to provision.
+        """
+        DirectoryProvisioningResource.__init__(
+            self,
+            parent.fp.child(uidsResourceName).path,
+            joinURL(parent.principalCollectionURL(), uidsResourceName) + "/",
+            parent.directory
+        )
+
+        self.parent = parent
+
+    def principalForUID(self, uid):
+        return self.parent.principalForUID(uid)
+
+    def principalForCalendarUserAddress(self, address):
+        return self.parent.principalForCalendarUserAddress(address)
+
+    ##
+    # Static
+    ##
+
+    def createSimilarFile(self, path):
+        log.err("Attempt to create clone %r of resource %r" % (path, self))
+        raise HTTPError(responsecode.NOT_FOUND)
+
+    def getChild(self, name):
+        self.provision()
+        if name == "":
+            return self
+
+        if "#" in name:
+            # This UID belongs to a sub-principal
+            primaryUID, subType = name.split("#")
+        else:
+            primaryUID = name
+            subType = None
+
+        record = self.directory.recordWithGUID(primaryUID)
+
         if record is None:
-            record = self.directory.recordWithShortName(self.recordType, name)
-            if record is None:
-                #log.err("No directory record (%s)%s; cannot create principal resource." % (self.recordType, name))
-                return None
+            log.err("No principal found for UID: %s" % (name,))
+            return None
+
+        primaryPrincipal = DirectoryPrincipalResource(self.fp.child(name).path, self, record)
+
+        if subType is None:
+            return primaryPrincipal
         else:
-            assert name is None
-            name = record.shortName
+            return primaryPrincipal.getChild(subType)
 
-        return DirectoryPrincipalResource(self.fp.child(name).path, self, record)
-
     def listChildren(self):
-        return (record.shortName for record in self.directory.listRecords(self.recordType))
+        # Not a listable collection
+        raise HTTPError(responsecode.FORBIDDEN)
 
     ##
     # ACL
@@ -275,18 +344,32 @@
         @param parent: the parent of this resource.
         @param record: the L{IDirectoryRecord} that this resource represents.
         """
-        super(DirectoryPrincipalResource, self).__init__(path, joinURL(parent.principalCollectionURL(), record.shortName))
+        if self.isCollection():
+            slash = "/"
+        else:
+            slash = ""
 
+        assert record is not None, "Principal must have a directory record: %s" % (path,)
+
+        url = joinURL(parent.principalCollectionURL(), record.guid) + slash
+
+        super(DirectoryPrincipalResource, self).__init__(path, url)
+
         self.record = record
         self.parent = parent
-        self._url = joinURL(parent.principalCollectionURL(), record.shortName)
-        if self.isCollection():
-            self._url += "/"
+        self._url   = url
 
+        self._alternate_urls = (
+            joinURL(parent.parent.principalCollectionURL(), record.recordType, record.shortName) + slash,
+        )
+
         # Provision in __init__() because principals are used prior to request
         # lookups.
         self.provision()
 
+    def __str__(self):
+        return "(%s) %s" % (self.record.recordType, self.record.shortName)
+
     ##
     # HTTP
     ##
@@ -306,6 +389,12 @@
                     yield "  ** %s **: %s\n" % (e.__class__.__name__, e)
             return "".join(genlist())
 
+        def format_principals(principals):
+            return format_list(
+                """<a href="%s">%s</a>""" % (principal.principalURL(), escape(str(principal)))
+                for principal in principals
+            )
+
         def link(url):
             return """<a href="%s">%s</a>""" % (url, url)
 
@@ -327,9 +416,9 @@
                 """Full name: %s\n"""              % (self.record.fullName,),
                 """Principal UID: %s\n"""          % (self.principalUID(),),
                 """Principal URL: %s\n"""          % (link(self.principalURL()),),
-                """\nAlternate URIs:\n"""          , format_list(self.alternateURIs()),
-                """\nGroup members:\n"""           , format_list(link(p.principalURL()) for p in self.groupMembers()),
-                """\nGroup memberships:\n"""       , format_list(link(p.principalURL()) for p in self.groupMemberships()),
+                """\nAlternate URIs:\n"""          , format_list(link(u) for u in self.alternateURIs()),
+                """\nGroup members:\n"""           , format_principals(self.groupMembers()),
+                """\nGroup memberships:\n"""       , format_principals(self.groupMemberships()),
                 """\nCalendar homes:\n"""          , format_list(link(u) for u in self.calendarHomeURLs()),
                 """\nCalendar user addresses:\n""" , format_list(link(a) for a in self.calendarUserAddresses()),
                 """</pre></blockquote></div>""",
@@ -356,7 +445,7 @@
 
     def alternateURIs(self):
         # FIXME: Add API to IDirectoryRecord for getting a record URI?
-        return ()
+        return self._alternate_urls
 
     def principalURL(self):
         return self._url
@@ -374,11 +463,8 @@
             myRecordType = self.record.recordType
             for relative in getattr(record, method)():
                 if relative not in records:
-                    if relative.recordType == myRecordType: 
-                        found = self.parent.getChild(None, record=relative)
-                    else:
-                        found = self.parent.parent.getChild(relative.recordType).getChild(None, record=relative)
-                    
+                    found = self.parent.principalForRecord(relative)
+
                     if proxy:
                         found = found.getChild("calendar-proxy-write")
                     relatives.add(found)
@@ -434,14 +520,18 @@
         return self.record.guid
         
     def calendarUserAddresses(self):
-        # Add the principal URL and GUID to whatever calendar user addresses
-        # the directory record provides.
+        # Get any CUAs defined by the directory implementation.
         addresses = set(self.record.calendarUserAddresses)
-        addresses.add(self.principalURL())
-        if config.HTTPPort:
-            addresses.add("http://%s:%s%s" % (config.ServerHostName, config.HTTPPort, self.principalURL(),))
-        if config.SSLPort:
-            addresses.add("https://%s:%s%s" % (config.ServerHostName, config.SSLPort, self.principalURL(),))
+
+        # Add the principal URL and alternate URIs to the list.
+        for uri in ((self.principalURL(),) + tuple(self.alternateURIs())):
+            addresses.add(uri)
+            if config.HTTPPort:
+                addresses.add("http://%s:%s%s" % (config.ServerHostName, config.HTTPPort, uri))
+            if config.SSLPort:
+                addresses.add("https://%s:%s%s" % (config.ServerHostName, config.SSLPort, uri))
+
+        # Add a UUID URI based on the record's GUID to the list.
         addresses.add("urn:uuid:%s" % (self.record.guid,))
 
         return addresses
@@ -515,7 +605,7 @@
         log.err("Attempt to create clone %r of resource %r" % (path, self))
         raise HTTPError(responsecode.NOT_FOUND)
 
-    def getChild(self, name, record=None):
+    def getChild(self, name):
         if name == "":
             return self
 

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/test/test_principal.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/test/test_principal.py	2007-08-21 00:35:50 UTC (rev 1809)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/test/test_principal.py	2007-08-21 02:22:46 UTC (rev 1810)
@@ -38,7 +38,7 @@
 from twistedcaldav.directory.xmlfile import XMLDirectoryService
 from twistedcaldav.directory.test.test_xmlfile import xmlFile
 from twistedcaldav.directory.principal import DirectoryPrincipalProvisioningResource
-from twistedcaldav.directory.principal import DirectoryPrincipalTypeResource
+from twistedcaldav.directory.principal import DirectoryPrincipalTypeProvisioningResource
 from twistedcaldav.directory.principal import DirectoryPrincipalResource
 
 import twistedcaldav.test.util
@@ -80,10 +80,10 @@
         DirectoryPrincipalProvisioningResource.principalCollectionURL(),
         DirectoryPrincipalProvisioningResource.principalCollections()
 
-        DirectoryPrincipalTypeResource.listChildren(),
-        DirectoryPrincipalTypeResource.getChildren(),
-        DirectoryPrincipalTypeResource.principalCollectionURL(),
-        DirectoryPrincipalTypeResource.principalCollections()
+        DirectoryPrincipalTypeProvisioningResource.listChildren(),
+        DirectoryPrincipalTypeProvisioningResource.getChildren(),
+        DirectoryPrincipalTypeProvisioningResource.principalCollectionURL(),
+        DirectoryPrincipalTypeProvisioningResource.principalCollections()
 
         DirectoryPrincipalResource.principalURL(),
         """
@@ -103,7 +103,7 @@
             for recordType in recordTypes:
                 #print "   -> %s" % (recordType,)
                 typeResource = provisioningResource.getChild(recordType)
-                self.failUnless(isinstance(typeResource, DirectoryPrincipalTypeResource))
+                self.failUnless(isinstance(typeResource, DirectoryPrincipalTypeProvisioningResource))
 
                 typeURL = provisioningURL + recordType + "/"
                 self.assertEquals(typeURL, typeResource.principalCollectionURL())
@@ -120,7 +120,7 @@
                     self.failUnless(isinstance(recordResource, DirectoryPrincipalResource))
 
                     recordURL = typeURL + shortName + "/"
-                    self.assertEquals(recordURL, recordResource.principalURL())
+                    self.assertIn(recordURL, (recordResource.principalURL(),) + tuple(recordResource.alternateURIs()))
 
                     principalCollections = recordResource.principalCollections()
                     self.assertEquals(set((provisioningURL,)), set(pc.principalCollectionURL() for pc in principalCollections))
@@ -202,7 +202,7 @@
             if record.shortName == "gemini":
                 self.assertTrue(principal.autoSchedule())
 
-    # FIXME: Run DirectoryPrincipalProvisioningResource tests on DirectoryPrincipalTypeResource also
+    # FIXME: Run DirectoryPrincipalProvisioningResource tests on DirectoryPrincipalTypeProvisioningResource also
 
     ##
     # DirectoryPrincipalResource

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/xmlaccountsparser.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/xmlaccountsparser.py	2007-08-21 00:35:50 UTC (rev 1809)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/xmlaccountsparser.py	2007-08-21 02:22:46 UTC (rev 1810)
@@ -168,6 +168,10 @@
             shortName = self.shortName % ctr
         else:
             shortName = self.shortName
+        if self.guid and self.guid.find("%") != -1:
+            guid = self.guid % ctr
+        else:
+            guid = self.guid
         if self.password.find("%") != -1:
             password = self.password % ctr
         else:
@@ -185,6 +189,7 @@
         
         result = XMLAccountRecord(self.recordType)
         result.shortName = shortName
+        result.guid = guid
         result.password = password
         result.name = name
         result.members = self.members

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/xmlfile.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/xmlfile.py	2007-08-21 00:35:50 UTC (rev 1809)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/directory/xmlfile.py	2007-08-21 02:22:46 UTC (rev 1810)
@@ -83,8 +83,11 @@
         return None
 
     def _entriesForRecordType(self, recordType):
-        for entry in self._accounts()[recordType].itervalues():
-             yield entry.shortName, entry
+        try:
+            for entry in self._accounts()[recordType].itervalues():
+                yield entry.shortName, entry
+        except KeyError:
+            return
 
     def _accounts(self):
         self.xmlFile.restat()

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/root.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/root.py	2007-08-21 00:35:50 UTC (rev 1809)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/root.py	2007-08-21 02:22:46 UTC (rev 1810)
@@ -86,17 +86,24 @@
             request.authzUser = authzUser
 
             # Figure out the "username" from the davxml.Principal object
-            username = authzUser.children[0].children[0].data
-            username = username.rstrip('/').split('/')[-1]
+            request.checkingSACL = True
+            d = request.locateResource(authzUser.children[0].children[0].data)
             
-            if RootResource.CheckSACL(username, self.saclService) != 0:
-                log.msg("User '%s' is not enabled with the '%s' SACL" % (username, self.saclService,))
-                return Failure(HTTPError(403))
+            def _checkedSACLCb(principal):
+                delattr(request, "checkingSACL")
+                username = principal.record.shortName
+                
+                if RootResource.CheckSACL(username, self.saclService) != 0:
+                    log.msg("User '%s' is not enabled with the '%s' SACL" % (username, self.saclService,))
+                    return Failure(HTTPError(403))
+    
+                # Mark SACL's as having been checked so we can avoid doing it multiple times
+                request.checkedSACL = True
+                return True
+            
+            d.addCallback(_checkedSACLCb)
+            return d
 
-            # Mark SACL's as having been checked so we can avoid doing it multiple times
-            request.checkedSACL = True
-            return True
-            
         d = defer.maybeDeferred(self.authenticate, request)
         d.addCallbacks(_authCb, _authEb)
         d.addCallback(_checkSACLCb)
@@ -106,7 +113,7 @@
         for filter in self.contentFilters:
             request.addResponseFilter(filter[0], atEnd=filter[1])
 
-        if self.useSacls and not hasattr(request, "checkedSACL"):
+        if self.useSacls and not hasattr(request, "checkedSACL") and not hasattr(request, "checkingSACL"):
             d = self.checkSacl(request)
             d.addCallback(lambda _: super(RootResource, self
                                           ).locateChild(request, segments))

Modified: CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/test/test_root.py
===================================================================
--- CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/test/test_root.py	2007-08-21 00:35:50 UTC (rev 1809)
+++ CalendarServer/branches/release/CalendarServer-1.0-dev/twistedcaldav/test/test_root.py	2007-08-21 02:22:46 UTC (rev 1810)
@@ -141,7 +141,7 @@
 
             self.assertEquals(request.authzUser, 
                               davxml.Principal(
-                    davxml.HRef('/principals/users/dreid/')))
+                    davxml.HRef('/principals/__uids__/5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1/')))
             
         d = defer.maybeDeferred(resrc.locateChild, request, ['principals'])
         d.addCallback(_Cb)
@@ -169,6 +169,7 @@
                                          ['principals'])
 
         def _Eb(failure):
+            failure.trap(HTTPError)
             self.assertEquals(failure.value.response.code, 403)
             
         d = defer.maybeDeferred(resrc.locateChild, request, ['principals'])
@@ -198,7 +199,6 @@
 
         def _Eb(failure):
             failure.trap(HTTPError)
-
             self.assertEquals(failure.value.response.code, 401)
 
         d = defer.maybeDeferred(resrc.locateChild, request, ['principals'])
@@ -229,6 +229,7 @@
                                          ['principals'])
 
         def _Eb(failure):
+            failure.trap(HTTPError)
             self.assertEquals(failure.value.response.code, 401)
             
         d = defer.maybeDeferred(resrc.locateChild, request, ['principals'])
@@ -275,4 +276,4 @@
             headers=http_headers.Headers({"Destination":"/copy/"})
         )
         return self.send(request, do_test)
-        
\ No newline at end of file
+        

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


More information about the calendarserver-changes mailing list