[CalendarServer-changes] [383] CalendarServer/branches/users/wsanchez/provisioning/twistedcaldav/ directory/resource.py

source_changes at macosforge.org source_changes at macosforge.org
Mon Nov 6 19:32:37 PST 2006


Revision: 383
          http://trac.macosforge.org/projects/calendarserver/changeset/383
Author:   wsanchez at apple.com
Date:     2006-11-06 19:32:36 -0800 (Mon, 06 Nov 2006)

Log Message:
-----------
New principal provisioning classes.

Modified Paths:
--------------
    CalendarServer/branches/users/wsanchez/provisioning/twistedcaldav/directory/resource.py

Modified: CalendarServer/branches/users/wsanchez/provisioning/twistedcaldav/directory/resource.py
===================================================================
--- CalendarServer/branches/users/wsanchez/provisioning/twistedcaldav/directory/resource.py	2006-11-07 03:32:18 UTC (rev 382)
+++ CalendarServer/branches/users/wsanchez/provisioning/twistedcaldav/directory/resource.py	2006-11-07 03:32:36 UTC (rev 383)
@@ -21,962 +21,133 @@
 """
 
 __all__ = [
-    "DirectoryPrincipalFile",
-    "DirectoryUserPrincipalProvisioningResource",
-    "DirectoryGroupPrincipalProvisioningResource",
-    "DirectoryResourcePrincipalProvisioningResource",
     "DirectoryPrincipalProvisioningResource",
+    "DirectoryPrincipalTypeResource",
+    "DirectoryPrincipalResource",
 ]
 
 from twisted.python import log
-from twisted.internet import reactor
-from twisted.internet import task
-from twisted.internet.defer import succeed
-from twisted.cred import credentials
-from twisted.web2 import responsecode
-from twisted.web2.dav import davxml
 from twisted.web2.dav.static import DAVFile
 from twisted.web2.dav.util import joinURL
-from twisted.web2.http import HTTPError
-from twisted.web2.http import StatusResponse
 
-from twistedcaldav import caldavxml
-from twistedcaldav import customxml
-from twistedcaldav.principalindex import GroupIndex
-from twistedcaldav.principalindex import ResourceIndex
-from twistedcaldav.principalindex import UserIndex
+from twistedcaldav.extensions import ReadOnlyResourceMixIn
 from twistedcaldav.resource import CalendarPrincipalCollectionResource
 from twistedcaldav.static import CalendarPrincipalFile
+from twistedcaldav.directory.idirectory import IDirectoryService
 
-import dsattributes
-import opendirectory
-import os
-import unicodedata
+# FIXME: These should be tied to DAVFile
 
-class DirectoryPrincipalFile (CalendarPrincipalFile):
+class DirectoryPrincipalProvisioningResource (ReadOnlyResourceMixIn, CalendarPrincipalCollectionResource, DAVFile):
     """
-    Directory principal resource.
+    Collection resource which provisions directory principals as its children.
     """
-    def __init__(self, parent, path, url):
+    def __init__(self, path, url, directory):
         """
         @param path: the path to the file which will back the resource.
-        @param url: the primary URL for the resource.  This is the url which
-            will be returned by L{principalURL}.
+        @param url: the canonical URL for the resource.
         """
-        super(DirectoryPrincipalFile, self).__init__(path, url)
-
-        self._parent = parent
-
-    def checkCredentials(self, creds):
-        """
-        Check whether the provided credentials can be used to authenticate this prinicpal.
-        
-        @param creds: the L{ICredentials} for testing.
-        @return:      True if the credentials match, False otherwise
-        """
-
-        # If there is no calendar principal URI then the calendar user is disabled.
-        if not self.hasDeadProperty(customxml.TwistedCalendarPrincipalURI):
-            return False
-
-        if isinstance(creds, credentials.UsernamePassword):
-            return opendirectory.authenticateUser(self._parent.directory, self.fp.basename(), creds.password)
-        else:
-            return False
-
-    def directory(self):
-        """
-        Get the directory object used for directory operations.
-        
-        @return:      C{object} for the directory instance
-        """
-
-        return self._parent.directory
-
-    def groupMembers(self):
-        """
-        See L{IDAVPrincipalResource.groupMembers}.
-        """
-        
-        # Check for the list of group member GUIDs
-        if self.hasDeadProperty(customxml.TwistedGroupMemberGUIDs):
-            # Get the list of GUIDs from the WebDAV private property
-            memberguids = self.readDeadProperty(customxml.TwistedGroupMemberGUIDs())
-            guids = [str(e) for e in memberguids.children]
-            
-            # Try to find each GUID in collections
-            result = []
-            for guid in guids:
-                uri = DirectoryTypePrincipalProvisioningResource.findAnyGUID(guid)
-                if uri is not None:
-                    result.append(uri)
-            
-            return result            
-
-        return ()
-
-    def groupMemberships(self):
-        """
-        See L{IDAVPrincipalResource.groupMemberships}.
-        """
-        
-        # Find any groups that match this user's GUID
-        guid = self.getGUID()
-        return DirectoryTypePrincipalProvisioningResource.findAnyGroupGUID(guid)
-
-    def getPropertyValue(self, cls):
-        """
-        Get the requested proeprty value or return empty string.
-        """
-        
-        if self.hasDeadProperty(cls()):
-            prop = self.readDeadProperty(cls())
-            return str(prop)
-        else:
-            return ""
-    
-    def setPropertyValue(self, str, cls):
-        """
-        Set the requested property value or remove it if the value is empty.
-        """
-
-        if str:
-            self.writeDeadProperty(cls.fromString(str))
-        else:
-            self.removeDeadProperty(cls())
-
-    def getGUID(self):
-        return self.getPropertyValue(customxml.TwistedGUIDProperty)
-    
-    def readProperty(self, property, request):
-        if type(property) is tuple:
-            qname = property
-        else:
-            qname = property.qname()
-
-        namespace, name = qname
-
-        if namespace == caldavxml.caldav_namespace:
-            if name == "calendar-user-address-set":
-                return succeed(caldavxml.CalendarUserAddressSet(
-                    *[davxml.HRef().fromString(uri) for uri in self.calendarUserAddresses()]
-                ))
-
-        return super(DirectoryPrincipalFile, self).readProperty(qname, request)
-
-    def writeProperty(self, property, request):
-        # This resource is read-only.
-        raise HTTPError(StatusResponse(
-            responsecode.FORBIDDEN,
-            "Protected property %s may not be set." % (property.sname(),)
-        ))
-
-    def calendarUserAddresses(self):
-        # Must have a valid calendar principal uri
-        if self.hasDeadProperty(customxml.TwistedCalendarPrincipalURI):
-            return (self.getPropertyValue(customxml.TwistedCalendarPrincipalURI),)
-        else:
-            # If there is no calendar principal URI then the calendar user is disabled so do not provide
-            # a valid calendar address.
-            return ()
-
-    def matchesCalendarUserAddress(self, request, address):
-        # By default we will always allow either a relative or absolute URI to the principal to
-        # be supplied as a valid calendar user address.
-
-        # Try calendar principal URI
-        return self.hasDeadProperty(customxml.TwistedCalendarPrincipalURI) and (self.getPropertyValue(customxml.TwistedCalendarPrincipalURI) == address)
-
-    def enable(self, calhome, enable):
-        """
-        Enable or disable this principal and access to any calendars owned by it.
-        
-        @param calhome: L{DAVFile} for the container of the calendar home of this user.
-        @param enable: C{True} to enable, C{False} to disable.
-        """
-        # Get home collection resource
-        calrsrc = calhome.getChild(self.principalUID())
-
-        # Handle access for the calendar home
-        if enable:
-            calrsrc.disable(False)
-        else:
-            calrsrc.disable(True)
-
-    def remove(self, calhome):
-        """
-        Remove this principal by hiding (archiving) any calendars owned by it. This is done
-        by turning on the disabling and renaming the calendar home to ensure a future user
-        with the same id won't see the old calendars.
-        
-        @param calhome: L{DAVFile} for the container of the calendar home of this user.
-        """
-        
-        # Get home collection resource
-        calrsrc = calhome.getChild(self.principalUID())
-
-        # Disable access for the calendar home
-        calrsrc.disable(True)
-        
-        # Rename the calendar home to the original name with the GUID appended
-        newname = self.principalUID() + "-" + self.getPropertyValue(customxml.TwistedGUIDProperty)
-        
-        try:
-            # Make sure the new name is not already in use
-            if os.path.exists(newname):
-                count = 1
-                tempname = newname + "-%d"
-                while(os.path.exists(tempname % count)):
-                    count += 1
-                newname = tempname % count 
-            os.rename(calrsrc.fp.path, calrsrc.fp.sibling(newname).path)
-        except OSError:
-            log.msg("Directory: Failed to rename %s to %s when deleting a principal" %
-                    (calrsrc.fp.path, calrsrc.fp.sibling(newname).path))
-            
-            # Remove the disabled property to prevent lock out in the future
-            calrsrc.disable(False)
-
-class DirectoryTypePrincipalProvisioningResource (CalendarPrincipalCollectionResource, DAVFile):
-    """
-    L{DAVFile} resource which provisions user L{CalendarPrincipalFile} resources
-    as needed.
-    
-    This includes a periodic task for refreshing the cached data.
-    """
-    periodicSyncIntervalSeconds = 60.0
-    
-    typeUnknown  = 0
-    typeUser     = 1
-    typeGroup    = 2
-    typeResource = 3
-
-    def __init__(self, path, url):
-        """
-        @param path: the path to the file which will back the resource.
-        @param url: the primary URL for the resource.  Provisioned child
-            resources will use a URL based on C{url} as their primary URLs.
-        @param directory: the reference to the directory to use
-        """
         CalendarPrincipalCollectionResource.__init__(self, url)
         DAVFile.__init__(self, path)
-        self.directory = None
-        self.calendarhomeroot = None
-        self.index = None
-        self.type = DirectoryTypePrincipalProvisioningResource.typeUnknown
 
-    def setup(self, directory):
-        self.directory = directory
+        self.directory = IDirectoryService(directory)
 
+    def createSimilarFile(self, path):
+        raise AssertionError("Not allowed.")
+
+    # FIXME: Remove
     def initialize(self, homeuri, home):
-        """
-        May be called during repository account initialization.
-         
-        @param homeuri: C{str} uri of the calendar home root.
-        @param home: L{DAVFile} of the calendar home root.
-        """
-        
-        # Make sure index is valid and sync with directory
-        self.index.check()
-        self.calendarhomeroot = (homeuri, home)
+        log.msg("*** Get rid of initialize() ***")
 
-        #
-        # There is a problem with the interaction of Directory Services and the
-        # fork/fork process the server goes through to daemonize itself. For some
-        # resaon, if DS is used before the fork, then calls to it afterwards all
-        # return eServerSendError (-14740) errors.
-        # 
-        # To get around this we must not use opendirectory module calls here, as this
-        # method gets run before the fork/fork. So instead, we schedule a sync
-        # operation to occur one second after the reactor starts up - which is after
-        # the fork/fork. The problem with this is that the http server is already up
-        # and running at that point BEFORE any initially provisioning is done.
-        #
+    def getChild(self, name):
+        if name == "":
+            return self
 
-        # Create a periodic sync operation to keep the cached user list
-        # in sync with the directory server.
-        def periodicSync(self):
-            self.syncNames()
-        
-        # Add initial sync operation
-        reactor.callLater(1.0, periodicSync, self) #@UndefinedVariable
+        if name not in self.listChildren():
+            return None
 
-        # Add periodic sync operations
-        runner = task.LoopingCall(periodicSync, self)
-        runner.start(DirectoryTypePrincipalProvisioningResource.periodicSyncIntervalSeconds, now=False)
-    
-    def listNames(self):
-        """
-        List all the names currently in the directory.
-
-        @return: C{list} containing C{str}'s for each name found, or C{None} if failed.
-        """
-        raise NotImplementedError
-
-    def listIndexAttributes(self):
-        """
-        List all the names currently in the directory with specific attributes needed for indexing.
-
-        @return: C{list} containing C{tuple}'s of C{str}'s for each entry found, or C{None} if failed.
-            The C{tuple} elements are: uid, guid, last-modified.
-        """
-        raise NotImplementedError
-
-    def listCommonAttributes(self, names):
-        """
-        List specified names currently in the directory returning useful attributes.
-
-        @param names: C{list} of record entries to list attributes for.
-        @return: C{dict} with keys for each entry found, and a C{dict} value containg the attributes,
-            or C{None} on failure.
-        """
-        raise NotImplementedError
-
-    def validName(self, name):
-        """
-        Verify that the supplied name exists as an entry in the directory and that the
-        name corresponds to one that can use the calendar.
-
-        @param name: C{str} of the name to check.
-        @return: C{True} if the name exists, C{False} otherwise.
-        """
-        raise NotImplementedError
-
-    def directoryAttributes(self, name):
-        """
-        Return the attributes relevant to the directory entry.
-        
-        @param name: C{str} containing the name for the directory entry.
-        @return: C{dict} containing the attribute key/value map.
-        """
-        raise NotImplementedError
-
-    def syncNames(self):
-        """
-        Synchronize the data in the directory with the local cache of resources in the file system.
-        """
-        #log.msg("Directory: Synchronizing cache for %s" % (self.getTitle(),))
-
-        # Get index entries from directory and from cache
-        remoteindex = self.listIndexAttributes()
-        localindex = self.index.listIndex()
-
-        # Create dicts indexed by GUID for each
-        remotedict = {}
-        for entry in remoteindex:
-            remotedict[entry[dsattributes.indexGUID]] = entry
-        localdict = {}
-        for entry in localindex:
-            localdict[entry[dsattributes.indexGUID]] = entry
-
-        # Extract list of GUIDs in each
-        remoteguids = [entry[dsattributes.indexGUID] for entry in remoteindex]
-        localguids = [entry[dsattributes.indexGUID] for entry in localindex]
-
-        remoteguidset = set(remoteguids)
-        remoteguids = None
-        localguidset = set(localguids)
-        localguids = None
-        
-        new_remote = list(remoteguidset.difference(localguidset))
-        removed_remote = list(localguidset.difference(remoteguidset))
-        
-        # Remove old principals
-        old_names = [localdict[guid][dsattributes.indexUID] for guid in removed_remote]
-        old_names.sort()
-        for name in old_names:
-            self.removePrincipal(name, True)
-
-        # Get new ones (but only those with a CalendarPrincipalURI attribute)
-        new_names = [remotedict[guid][dsattributes.indexUID] for guid in new_remote if remotedict[guid][dsattributes.indexCalendarPrincipalURI]]
-
-        # Get all the directory entries for the new ones in one go for better performance
-        if new_names:
-            new_entries = self.listCommonAttributes(new_names)
-            new_names = [n for n in new_entries.iterkeys()]
-            new_names.sort()
-            for name in new_names:
-                self.addPrincipal(name, attrs=new_entries[name], fast=True)
-            
-        # Look for changes in entries
-        common_entries = list(remoteguidset.intersection(localguidset))
-        for guid in common_entries:
-            old = localdict[guid]
-            new = remotedict[guid]
-            if old != new:
-                # Special case issue with unicode normalization of names
-                if ((old[dsattributes.indexUID] != new [dsattributes.indexUID]) and
-                    (unicodedata.normalize("NFKC", old[dsattributes.indexUID].decode("UTF-8")) ==
-                     unicodedata.normalize("NFKC", new[dsattributes.indexUID].decode("UTF-8"))) and
-                    (old[1:] == new[1:])):
-                    continue
-                
-                self.changedPrincipal(old, new)
-        
-        # Commit index after all changes are done
-        self.index.commit()
-
-    def addPrincipal(self, name, attrs=None, fast=False):
-        """
-        Add a new principal resource to the server.
-        
-        @param name: C{str} containing the name of the resource to add.
-        @param attrs: C{dict} directory attributes for this name, or C{None} if attributes need to be read in.
-        @param fast: if C{True} then final commit is not done, if C{False} commit is done.
-        """
-        # This will create it
         child_fp = self.fp.child(name)
-        assert not child_fp.exists()
-
-        assert self.exists()
-        assert self.isCollection()
-
-        child_fp.open("w").close()
-        
-        # Now update the principal's cached data
-        self.updatePrincipal(name, attrs=attrs, fast=fast, new=True, nolog=True)
-        
-        log.msg("Directory: Add %s to %s" % (name, self.getTitle()))
-    
-    def changedPrincipal(self, old, new, fast=False):
-        """
-        Change a new principal resource to sync with directory.
-        
-        @param old: C{str} containing the name of the original resource.
-        @param new: C{str} containing the name of the new resource.
-        @param fast: if C{True} then final commit is not done, if C{False} commit is done.
-        """
-        # Look for change to calendar enabled state
-        
-        # See if the name changed because that is a real pain!
-        if ((old[dsattributes.indexUID] != new[dsattributes.indexUID]) and
-            (unicodedata.normalize("NFKC", old[dsattributes.indexUID].decode("UTF-8")) ==
-             unicodedata.normalize("NFKC", new[dsattributes.indexUID].decode("UTF-8")))):
-            self.renamePrincipal(old[dsattributes.indexUID], new[dsattributes.indexUID])
-        
-        # See if change in enable state
-        enable_state = old[dsattributes.indexCalendarPrincipalURI] != new[dsattributes.indexCalendarPrincipalURI]
-        
-        # Do update (no log message if enable state is being changed as that will generate a log message itself)
-        self.updatePrincipal(new[dsattributes.indexUID], nolog=enable_state)
-
-        # Look for change in calendar enable state
-        if enable_state:
-            self.enablePrincipal(new[dsattributes.indexUID], len(new[dsattributes.indexCalendarPrincipalURI]) != 0)
-
-    def renamePrincipal(self, old, new):
-        """
-        Change a principal resource name to sync with directory.
-        
-        @param old: C{str} containing the name of the original resource.
-        @param new: C{str} containing the name of the new resource.
-        """
-        log.msg("Directory: Renamed Principal %s to %s in %s" % (old, new, self.getTitle()))
-        raise NotImplementedError
-    
-    def updatePrincipal(self, name, attrs = None, fast=False, new=False, nolog=False):
-        """
-        Update details about the named principal in the principal's own property store
-        and the principal collection index.
-        
-        @param name: C{str} containing the principal name to update.
-        @param attrs: C{dict} directory attributes for this name, or C{None} if attributes need to be read in.
-        @param fast: if C{True} then final commit is not done, if C{False} commit is done.
-        @param new: C{True} when this update is the result of adding a new principal,
-            C{False} otherwise.
-        """
-        # Get attributes from directory
-        if attrs is None:
-            attrs = self.directoryAttributes(name)
-        realname = attrs.get(dsattributes.attrRealName, None)
-        guid = attrs.get(dsattributes.attrGUID, None)
-        lastModified = attrs.get(dsattributes.attrLastModified, None)
-        principalurl = attrs.get(dsattributes.attrCalendarPrincipalURI, None)
-
-        # Do provisioning (if the principal is a resource we will turn on the auto-provisioning option)
-        principal = self.getChild(name)
-        if principal is None:
-            log.msg("Directory: Failed to update missing principal: %s in %s" % (name, self.getTitle()))
-            return
-
-        if (new):
-            principal.provisionCalendarAccount(name, None, True, None, self.calendarhomeroot,
-                                               None, ["calendar"], self.type == DirectoryTypePrincipalProvisioningResource.typeResource)
-        
-        # Add directory specific attributes to principal
-        principal.setPropertyValue(realname, davxml.DisplayName)
-        principal.setPropertyValue(guid, customxml.TwistedGUIDProperty)
-        principal.setPropertyValue(lastModified, customxml.TwistedLastModifiedProperty)
-        principal.setPropertyValue(principalurl, customxml.TwistedCalendarPrincipalURI)
-        
-        # Special for group
-        if self.type == DirectoryTypePrincipalProvisioningResource.typeGroup:
-            # Get comma separated list of members and split into a list
-            groupmembers = attrs.get(dsattributes.attrGroupMembers, None)
-            if isinstance(groupmembers, list):
-                members = groupmembers
-            elif isinstance(groupmembers, str):
-                members = [groupmembers]
-            else:
-                members = []
-            
-            # Create and write the group property
-            children = [customxml.TwistedGUIDProperty.fromString(s) for s in members]
-            principal.writeDeadProperty(customxml.TwistedGroupMemberGUIDs(*children))
-        
-        # Do index
-        self.index.addPrincipal(name, principal, fast)
-
-        if not nolog:
-            log.msg("Directory: Updated %s in %s" % (name, self.getTitle()))
-    
-    def enablePrincipal(self, name, enable):
-        """
-        Enable or disable calendar access for this principal.
-        
-        @param enable: C{True} to enable, C{False} to disable
-        """
-        principal = self.getChild(name)
-        if principal is None:
-            log.msg("Directory: Failed to enable/disable missing principal: %s in %s" % (name, self.getTitle()))
-            return
-
-        if enable:
-            principal.enable(self.calendarhomeroot[1], True)
-            log.msg("Directory: Enabled %s in %s" % (name, self.getTitle()))
-        else:
-            principal.enable(self.calendarhomeroot[1], False)
-            log.msg("Directory: Disabled %s in %s" % (name, self.getTitle()))
-
-    def removePrincipal(self, name, fast=False):
-        """
-        Remove a principal from the cached resources.
-        
-        @param name: C{str} containing the name of the principal to remove.
-        @param fast: if C{True} then final commit is not done, if C{False} commit is done.
-        """
-        
-        # Get the principal to 'hide' its calendars
-        principal = self.getChild(name)
-        if principal is None:
-            log.msg("Directory: Failed to remove missing principal: %s in %s" % (name, self.getTitle()))
-        else:
-            principal.remove(self.calendarhomeroot[1])
-
-        # Now remove the principal resource itself
-        child_fp = self.fp.child(name)
         if child_fp.exists():
-            os.remove(child_fp.path)
-
-        # Do index
-        self.index.deleteName(name, fast)
-
-        log.msg("Directory: Delete %s from %s" % (name, self.getTitle()))
-    
-    @classmethod
-    def findAnyGUID(clazz, guid):
-        """
-        Find the principal associated with the specified GUID.
-
-        @param guid: the C{str} containing the GUID to match.
-        @return: C{str} with matching principal URI, or C{None}
-        """
-        for url in clazz._principleCollectionSet.keys():
-            try:
-                pcollection = clazz._principleCollectionSet[url]
-                if isinstance(pcollection, DirectoryTypePrincipalProvisioningResource):
-                    principal = pcollection.findGUID(guid)
-                    if principal is not None:
-                        return principal
-            except ReferenceError:
-                pass
-
-        return None
-
-    def findGUID(self, guid):
-        """
-        See if a principal with the specified GUID exists and if so return its principal URI.
-        
-        @param guid: the C{str} containing the GUID to match.
-        @return: C{str} with matching principal URI, or C{None}
-        """
-        name = self.index.nameFromGUID(guid)
-        if name is not None:
-            principal = self.getChild(name)
-            return principal.principalURL()
+            assert child_fp.isdir()
         else:
-            return None
+            assert self.exists()
+            assert self.isCollection()
 
-    @classmethod
-    def findAnyGroupGUID(clazz, guid):
-        """
-        Find the principals containing the specified GUID as a group member.
+            child_fp.makedirs()
 
-        @param guid: the C{str} containing the GUID to match.
-        @return: C{list} with matching principal URIs
-        """
-        
-        result = []
-        for url in clazz._principleCollectionSet.keys():
-            try:
-                pcollection = clazz._principleCollectionSet[url]
-                if isinstance(pcollection, DirectoryTypePrincipalProvisioningResource):
-                    result.extend(pcollection.findGroupGUID(guid))
-            except ReferenceError:
-                pass
+        return DirectoryPrincipalTypeResource(child_fp.path, self, name)
 
-        return result
+    def listChildren(self):
+        return self.directory.recordTypes()
 
-    def findGroupGUID(self, guid):
-        """
-        Find principals with the specified GUID as a group member.
-        
-        @param guid: the C{str} containing the GUID to match.
-        @return: C{list} with matching principal URIs
-        """
-        # Only both for group collections
-        if self.type != DirectoryTypePrincipalProvisioningResource.typeGroup:
-            return []
-        
-        result = []
-        for name in self.listChildren():
-            principal = self.getChild(name)
-            if principal.hasDeadProperty(customxml.TwistedGroupMemberGUIDs):
-                guids = principal.readDeadProperty(customxml.TwistedGroupMemberGUIDs)
-                for g in guids.children:
-                    if str(g) == guid:
-                        result.append(principal.principalURL())
-                        break
+class DirectoryPrincipalTypeResource (ReadOnlyResourceMixIn, CalendarPrincipalCollectionResource, DAVFile):
+    """
+    Collection resource which provisions directory principals of a specific type as its children.
+    """
+    def __init__(self, path, parent, name):
+        CalendarPrincipalCollectionResource.__init__(self, joinURL(parent.principalCollectionURL(), name))
+        DAVFile.__init__(self, path)
 
-        return result
+        self.directory = parent.directory
+        self.recordType = name
 
-    def isCollection(self):
-        """
-        See L{IDAVResource.isCollection}.
-        """
-        return True
+    def createSimilarFile(self, path):
+        raise AssertionError("Not allowed.")
 
     def getChild(self, name):
-        """
-        Look up a child resource.
-        @return: the child of this resource with the given name.
-        """
         if name == "":
             return self
 
-        child = self.putChildren.get(name, None)
-        if child: return child
+        record = self.directory.recordWithShortName(self.recordType, name)
+        if record is None:
+            return None
 
         child_fp = self.fp.child(name)
         if child_fp.exists():
-            return DirectoryPrincipalFile(self, child_fp.path, joinURL(self._url, name))
+            assert child_fp.isfile()
         else:
-            return None
+            assert self.exists()
+            assert self.isCollection()
 
-    def principalSearchPropertySet(self):
-        """
-        See L{IDAVResource.principalSearchPropertySet}.        
-        """
-        return davxml.PrincipalSearchPropertySet(
-            davxml.PrincipalSearchProperty(
-                davxml.PropertyContainer(
-                    davxml.DisplayName()
-                ),
-                davxml.Description(
-                    davxml.PCDATAElement("Display Name"),
-                    **{"xml:lang":"en"}
-                ),
-            ),
-            davxml.PrincipalSearchProperty(
-                davxml.PropertyContainer(
-                    caldavxml.CalendarUserAddressSet()
-                ),
-                davxml.Description(
-                    davxml.PCDATAElement("Calendar User Addresses"),
-                    **{"xml:lang":"en"}
-                ),
-            ),
-        )
+            child_fp.open("w").close()
 
-    def createSimilarFile(self, path):
-        if path == self.fp.path:
-            return self
-        else:
-            # TODO: Fix this - not sure how to get URI for second argument of __init__
-            return CalendarPrincipalFile(path, "")
+        return DirectoryPrincipalResource(child_fp.path, self, name)
 
-    def http_PUT        (self, request): return responsecode.FORBIDDEN
-    def http_MKCOL      (self, request): return responsecode.FORBIDDEN
-    def http_MKCALENDAR (self, request): return responsecode.FORBIDDEN
+    def listChildren(self):
+        return self.directory.listRecords(self.recordType)
 
-class DirectoryUserPrincipalProvisioningResource (DirectoryTypePrincipalProvisioningResource):
+class DirectoryPrincipalResource (ReadOnlyResourceMixIn, CalendarPrincipalFile):
     """
-    L{DAVFile} resource which provisions user L{CalendarPrincipalFile} resources
-    as needed.
+    Directory principal resource.
     """
-    def __init__(self, path, url):
-        """
-        @param path: the path to the file which will back the resource.
-        @param url: the primary URL for the resource.  Provisioned child
-            resources will use a URL based on C{url} as their primary URLs.
-        @param directory: the reference to the directory to use
-        """
-        DirectoryTypePrincipalProvisioningResource.__init__(self, path, url)
-        self.index = UserIndex(self)
-        self.type = DirectoryTypePrincipalProvisioningResource.typeUser
+    def __init__(self, path, parent, name):
+        super(DirectoryPrincipalResource, self).__init__(path, parent.principalCollectionURL())
 
-    def listNames(self):
-        """
-        List all the names currently in the directory.
+        self.directory = parent.directory
+        self.recordType = parent.recordType
+        self.shortName = name
 
-        @return: C{list} containg C{str}'s for each name found, or C{None} if failed.
-        """
+    ##
+    # ACL
+    ##
 
-        # Lookup all users
-        return [i[0] for i in opendirectory.listUsers(self.directory)]
- 
-    def listIndexAttributes(self):
-        """
-        List all the names currently in the directory with specific attributes needed for indexing.
+    def alternateURIs(self):
+        return ()
 
-        @return: C{list} containing C{tuple}'s of C{str}'s for each entry found, or C{None} if failed.
-            The C{tuple} elements are: uid, guid, last-modified.
-        """
-        return opendirectory.listUsers(self.directory)
+    def groupMembers(self):
+        raise NotImplementedError()
 
-    def listCommonAttributes(self, names):
-        """
-        List specified names currently in the directory returning useful attributes.
+    def groupMemberships(self):
+        raise NotImplementedError()
 
-        @param names: C{list} of record entries to list attributes for.
-        @return: C{dict} with keys for each entry found, and a C{dict} value containg the attributes,
-            or C{None} on failure.
-        """
-        return opendirectory.listUsersWithAttributes(self.directory, names)
+    ##
+    # CalDAV
+    ##
 
-    def validName(self, name):
-        """
-        Verify that the supplied name exists as an entry in the directory.
+    def principalUID(self):
+        return self.shortName
 
-        @param name: C{str} of the namer to check.
-        @return: C{True} if the name exists, C{False} otherwise.
-        """
-        return opendirectory.checkUser(self.directory, name)
+    def calendarHomeURLs(self):
+        raise NotImplementedError()
 
-    def directoryAttributes(self, name):
-        """
-        Return the attributes relevant to the directory entry.
-        
-        @param name: C{str} containing the name for the directory entry.
-        @return: C{dict} containing the attribute key/value map.
-        """
-        result = opendirectory.listUsersWithAttributes(self.directory, [name])
-        if result:
-            return result[name]
-        else:
-            return None
-
-    def getTitle(self):
-        return "User Principals"
-
-class DirectoryGroupPrincipalProvisioningResource (DirectoryTypePrincipalProvisioningResource):
-    """
-    L{DAVFile} resource which provisions user L{CalendarPrincipalFile} resources
-    as needed.
-    """
-    def __init__(self, path, url):
-        """
-        @param path: the path to the file which will back the resource.
-        @param url: the primary URL for the resource.  Provisioned child
-            resources will use a URL based on C{url} as their primary URLs.
-        @param directory: the reference to the directory to use
-        """
-        DirectoryTypePrincipalProvisioningResource.__init__(self, path, url)
-        self.index = GroupIndex(self)
-        self.type = DirectoryTypePrincipalProvisioningResource.typeGroup
-
-    def listNames(self):
-        """
-        List all the names currently in the directory.
-
-        @return: C{list} containg C{str}'s for each name found, or C{None} if failed.
-        """
-
-        # Lookup all users
-        return [i[0] for i in opendirectory.listGroups(self.directory)]
- 
-    def listIndexAttributes(self):
-        """
-        List all the names currently in the directory with specific attributes needed for indexing.
-
-        @return: C{list} containing C{tuple}'s of C{str}'s for each entry found, or C{None} if failed.
-            The C{tuple} elements are: uid, guid, last-modified.
-        """
-        return opendirectory.listGroups(self.directory)
-
-    def listCommonAttributes(self, names):
-        """
-        List specified names currently in the directory returning useful attributes.
-
-        @param names: C{list} of record entries to list attributes for.
-        @return: C{dict} with keys for each entry found, and a C{dict} value containg the attributes,
-            or C{None} on failure.
-        """
-        return opendirectory.listGroupsWithAttributes(self.directory, names)
-
-    def validName(self, name):
-        """
-        Verify that the supplied name exists as an entry in the directory.
-
-        @param name: C{str} of the namer to check
-        @return: C{True} if the name exists, C{False} otherwise
-        """
-        return opendirectory.checkGroup(self.directory, name)
-
-    def directoryAttributes(self, name):
-        """
-        Return the attributes relevant to the directory entry.
-        
-        @param name: C{str} containing the name for the directory entry.
-        @return: C{dict} containing the attribute key/value map.
-        """
-        result = opendirectory.listGroupsWithAttributes(self.directory, [name])
-        if result:
-            return result[name]
-        else:
-            return None
-
-    def getTitle(self):
-        return "Group Principals"
-
-class DirectoryResourcePrincipalProvisioningResource (DirectoryTypePrincipalProvisioningResource):
-    """
-    L{DAVFile} resource which provisions user L{CalendarPrincipalFile} resources
-    as needed.
-    """
-    def __init__(self, path, url):
-        """
-        @param path: the path to the file which will back the resource.
-        @param url: the primary URL for the resource.  Provisioned child
-            resources will use a URL based on C{url} as their primary URLs.
-        @param directory: the reference to the directory to use
-        """
-        DirectoryTypePrincipalProvisioningResource.__init__(self, path, url)
-        self.index = ResourceIndex(self)
-        self.type = DirectoryTypePrincipalProvisioningResource.typeResource
-
-    def listNames(self):
-        """
-        List all the names currently in the directory.
-
-        @return: C{list} containg C{str}'s for each name found, or C{None} if failed.
-        """
-
-        # Lookup all users
-        return [i[0] for i in opendirectory.listResources(self.directory)]
- 
-    def listIndexAttributes(self):
-        """
-        List all the names currently in the directory with specific attributes needed for indexing.
-
-        @return: C{list} containing C{tuple}'s of C{str}'s for each entry found, or C{None} if failed.
-            The C{tuple} elements are: uid, guid, last-modified.
-        """
-        return opendirectory.listResources(self.directory)
-
-    def listCommonAttributes(self, names):
-        """
-        List specified names currently in the directory returning useful attributes.
-
-        @param names: C{list} of record entries to list attributes for.
-        @return: C{dict} with keys for each entry found, and a C{dict} value containg the attributes,
-            or C{None} on failure.
-        """
-        return opendirectory.listResourcesWithAttributes(self.directory, names)
-
-    def validName(self, name):
-        """
-        Verify that the supplied name exists as an entry in the directory.
-
-        @param name: C{str} of the namer to check
-        @return: C{True} if the name exists, C{False} otherwise
-        """
-        return opendirectory.checkResource(self.directory, name)
-
-    def directoryAttributes(self, name):
-        """
-        Return the attributes relevant to the directory entry.
-        
-        @param name: C{str} containing the name for the directory entry.
-        @return: C{dict} containing the attribute key/value map.
-        """
-        result = opendirectory.listResourcesWithAttributes(self.directory, [name])
-        if result:
-            return result[name]
-        else:
-            return None
-
-    def getTitle(self):
-        return "Resource Principals"
-
-class DirectoryPrincipalProvisioningResource (DAVFile):
-    """
-    L{DAVFile} resource which provisions calendar principal resources as needed.
-    """
-    def __init__(self, path, url, params={}):
-        """
-        @param path: the path to the file which will back the resource.
-        @param url: the primary URL for the resource.  Provisioned child
-            resources will use a URL based on C{url} as their primary URLs.
-        """
-        super(DirectoryPrincipalProvisioningResource, self).__init__(path)
-
-        assert self.exists(), "%s should exist" % (self,)
-        assert self.isCollection(), "%s should be a collection" % (self,)
-
-        # Extract parameters
-        if (params.has_key("DirectoryNode")):
-            self.directory = opendirectory.odInit(params["DirectoryNode"])
-            if self.directory is None:
-                raise ValueError("Failed to open Open Directory Node: %s" % (params["DirectoryNode"],))
-        else:
-            raise ValueError("DirectoryPrincipalProvisioningResource must be configured with an Open Directory Node")
-
-        # Create children
-        for name, clazz in (
-            ("users" , DirectoryUserPrincipalProvisioningResource),
-            ("groups" , DirectoryGroupPrincipalProvisioningResource),
-            ("resources" , DirectoryResourcePrincipalProvisioningResource),
-        ):
-            child_fp = self.fp.child(name)
-            if not child_fp.exists(): child_fp.makedirs()
-            principalCollection = clazz(child_fp.path, joinURL(url, name) + "/")
-            principalCollection.setup(self.directory)
-            self.putChild(name, principalCollection)
-
-    def isCollection(self):
-        """
-        See L{IDAVResource.isCollection}.
-        """
-        return True
-
-    def initialize(self, homeuri, home):
-        """
-        May be called during repository account initialization.
-        This implementation does nothing.
-        
-        @param homeuri: C{str} uri of the calendar home root.
-        @param home: L{DAVFile} of the calendar home root.
-        """
-        for name in ("users", "groups", "resources",):
-            self.getChild(name).initialize(joinURL(homeuri, name), home.getChild(name))
-    
-    def createSimilarFile(self, path):
-        return DAVFile(path)
-
-    def render(self, request):
-        return StatusResponse(
-            responsecode.OK,
-            "This collection contains principal resources",
-            title=self.displayName()
-        )
+    def calendarUserAddresses(self):
+        raise NotImplementedError()

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


More information about the calendarserver-changes mailing list