[CalendarServer-changes] [761] CalendarServer/trunk/twistedcaldav/directory

source_changes at macosforge.org source_changes at macosforge.org
Fri Dec 8 01:43:19 PST 2006


Revision: 761
          http://trac.macosforge.org/projects/calendarserver/changeset/761
Author:   wsanchez at apple.com
Date:     2006-12-08 01:43:17 -0800 (Fri, 08 Dec 2006)

Log Message:
-----------
Require all directory services to provide a realm name.

Add a guid attribute to IDirectoryService and implement it on all
services by generating it from a class-specific GUID and the realm
name.

Generate a GUID for all directory records (if they don't already have
one) by generating it from the service's GUID and the record's type
and short name.

End result is that all principals now have a GUID which is a valid
UUID.

principalUID() is returns the principal's GUID in
DirectoryPrincipalResource.

Modified Paths:
--------------
    CalendarServer/trunk/twistedcaldav/directory/apache.py
    CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py
    CalendarServer/trunk/twistedcaldav/directory/directory.py
    CalendarServer/trunk/twistedcaldav/directory/idirectory.py
    CalendarServer/trunk/twistedcaldav/directory/principal.py
    CalendarServer/trunk/twistedcaldav/directory/sqldb.py
    CalendarServer/trunk/twistedcaldav/directory/test/test_apache.py
    CalendarServer/trunk/twistedcaldav/directory/test/test_principal.py
    CalendarServer/trunk/twistedcaldav/directory/test/test_xmlfile.py
    CalendarServer/trunk/twistedcaldav/directory/test/util.py
    CalendarServer/trunk/twistedcaldav/directory/util.py
    CalendarServer/trunk/twistedcaldav/directory/xmlfile.py

Modified: CalendarServer/trunk/twistedcaldav/directory/apache.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/apache.py	2006-12-08 08:01:19 UTC (rev 760)
+++ CalendarServer/trunk/twistedcaldav/directory/apache.py	2006-12-08 09:43:17 UTC (rev 761)
@@ -38,9 +38,9 @@
     Abstract Apache-compatible implementation of L{IDirectoryService}.
     """
     def __repr__(self):
-        return "<%s %r %r>" % (self.__class__.__name__, self.userFile, self.groupFile)
+        return "<%s %r: %r %r>" % (self.__class__.__name__, realmName, self.userFile, self.groupFile)
 
-    def __init__(self, userFile, groupFile=None):
+    def __init__(self, realmName, userFile, groupFile=None):
         super(AbstractDirectoryService, self).__init__()
 
         if type(userFile) is str:
@@ -48,6 +48,7 @@
         if type(groupFile) is str:
             groupFile = FilePath(groupFile)
 
+        self.realmName = realmName
         self.userFile = userFile
         self.groupFile = groupFile
 
@@ -163,6 +164,7 @@
     """
     Apache UserFile/GroupFile implementation of L{IDirectoryService}.
     """
+    baseGUID = "DDF1E45C-CADE-4FCD-8AE6-B4B41D72B325"
     userRecordClass = BasicUserRecord
 
 class DigestUserRecord(AbstractUserRecord):
@@ -176,6 +178,7 @@
     """
     Apache DigestUserFile/GroupFile implementation of L{IDirectoryService}.
     """
+    baseGUID = "0C719D1B-0A14-4074-8740-6D96A7D0C787"
     userRecordClass = DigestUserRecord
 
 class GroupRecord(AbstractDirectoryRecord):

Modified: CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py	2006-12-08 08:01:19 UTC (rev 760)
+++ CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py	2006-12-08 09:43:17 UTC (rev 761)
@@ -43,8 +43,10 @@
     """
     Open Directory implementation of L{IDirectoryService}.
     """
+    baseGUID = "891F8321-ED02-424C-BA72-89C32F215C1E"
+
     def __repr__(self):
-        return "<%s %s>" % (self.__class__.__name__, self.node)
+        return "<%s %r: %r>" % (self.__class__.__name__, self.realmName, self.node)
 
     def __init__(self, node="/Search"):
         """
@@ -54,6 +56,7 @@
         if directory is None:
             raise OpenDirectoryInitError("Failed to open Open Directory Node: %s" % (node,))
 
+        self.realmName = node
         self.directory = directory
         self.node = node
         self._records = {}

Modified: CalendarServer/trunk/twistedcaldav/directory/directory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/directory.py	2006-12-08 08:01:19 UTC (rev 760)
+++ CalendarServer/trunk/twistedcaldav/directory/directory.py	2006-12-08 09:43:17 UTC (rev 761)
@@ -29,14 +29,17 @@
 ]
 
 import sys
+from urllib import quote
 
 from zope.interface import implements
 
+from twisted.python import log
 from twisted.cred.error import UnauthorizedLogin
 from twisted.cred.checkers import ICredentialsChecker
 from twisted.web2.dav.auth import IPrincipalCredentials
 
 from twistedcaldav.directory.idirectory import IDirectoryService, IDirectoryRecord
+from twistedcaldav.directory.util import uuidFromName
 
 class DirectoryService(object):
     implements(IDirectoryService, ICredentialsChecker)
@@ -47,6 +50,25 @@
 
     realmName = None
 
+    def _generatedGUID(self):
+        if not hasattr(self, "_guid"):
+            realmName = self.realmName
+
+            assert self.baseGUID, "Class %s must provide a baseGUID attribute" % (self.__class__.__name__,)
+
+            if realmName is None:
+                log.err("Directory service %s has no realm name or GUID; generated service GUID will not be unique.")
+                realmName = ""
+            else:
+                log.err("Directory service %s has no GUID; generating service GUID from realm name.")
+
+            self._guid = uuidFromName(self.baseGUID, realmName)
+
+        return self._guid
+
+    baseGUID = None
+    guid = property(_generatedGUID)
+
     ##
     # ICredentialsChecker
     ##
@@ -67,7 +89,10 @@
             raise UnauthorizedLogin("No such user: %s" % (user,))
 
         if user.verifyCredentials(credentials.credentials):
-            return (credentials.authnPrincipal.principalURL(), credentials.authzPrincipal.principalURL())
+            return (
+                credentials.authnPrincipal.principalURL(),
+                credentials.authzPrincipal.principalURL(),
+            )
         else:
             raise UnauthorizedLogin("Incorrect credentials for %s" % (user,)) 
 
@@ -112,6 +137,15 @@
         )
 
     def __init__(self, service, recordType, guid, shortName, fullName, calendarUserAddresses):
+        assert service.realmName is not None
+        assert recordType
+        assert shortName
+
+        if not guid:
+            guid = uuidFromName(service.guid, "%s:%s" % (recordType, shortName))
+
+        calendarUserAddresses.add("urn:uuid:%s" % (guid,))
+
         self.service               = service
         self.recordType            = recordType
         self.guid                  = guid

Modified: CalendarServer/trunk/twistedcaldav/directory/idirectory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/idirectory.py	2006-12-08 08:01:19 UTC (rev 760)
+++ CalendarServer/trunk/twistedcaldav/directory/idirectory.py	2006-12-08 09:43:17 UTC (rev 761)
@@ -32,6 +32,7 @@
     Directory Service
     """
     realmName = Attribute("The name of the authentication realm this service represents.")
+    guid = Attribute("A GUID for this service.")
 
     def recordTypes():
         """

Modified: CalendarServer/trunk/twistedcaldav/directory/principal.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/principal.py	2006-12-08 08:01:19 UTC (rev 760)
+++ CalendarServer/trunk/twistedcaldav/directory/principal.py	2006-12-08 09:43:17 UTC (rev 761)
@@ -338,7 +338,7 @@
     ##
 
     def principalUID(self):
-        return self.record.shortName
+        return self.record.guid
         
     def calendarUserAddresses(self):
         # Add the principal URL to whatever calendar user addresses

Modified: CalendarServer/trunk/twistedcaldav/directory/sqldb.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/sqldb.py	2006-12-08 08:01:19 UTC (rev 760)
+++ CalendarServer/trunk/twistedcaldav/directory/sqldb.py	2006-12-08 09:43:17 UTC (rev 761)
@@ -56,30 +56,46 @@
     House keeping operations on the SQL DB, including loading from XML file,
     and record dumping. This can be used as a standalong DB management tool.
     """
+    dbType = "DIRECTORYSERVICE"
+    dbFilename = ".db.accounts"
+    dbFormatVersion = "1"
 
-    DBTYPE = "DIRECTORYSERVICE"
-    DBNAME = ".db.accounts"
-    DBVERSION = "1"
-    ACCOUNTDB = "ACCOUNTS"
-    GROUPSDB = "GROUPS"
-    CUADDRDB = "CUADDRS"
+    def _getRealmName(self):
+        #
+        # This is used frequently enough that it's worth caching.
+        # Downside is that changing the realm name (with SQL directly) requires
+        # a server restart.
+        #
+        if not hasattr(self, "_realmName"):
+            realmName = None
+            for row in self._db_execute("select REALM from SERVICE"):
+                assert realmName is None
+                realmName = row[0]
+            self._realmName = realmName
+        return self._realmName
 
+    realmName = property(_getRealmName)
+
     def __init__(self, path):
-        path = os.path.join(path, SQLDirectoryManager.DBNAME)
-        super(SQLDirectoryManager, self).__init__(path, SQLDirectoryManager.DBVERSION)
+        path = os.path.join(path, SQLDirectoryManager.dbFilename)
+        super(SQLDirectoryManager, self).__init__(path, SQLDirectoryManager.dbFormatVersion)
 
     def loadFromXML(self, xmlFile):
-       xmlAccounts = XMLAccountsParser(xmlFile)
+        parser = XMLAccountsParser(xmlFile)
        
-       # Totally wipe existing DB and start from scratch
-       if os.path.exists(self.dbpath):
-           os.remove(self.dbpath)
+        # Totally wipe existing DB and start from scratch
+        if os.path.exists(self.dbpath):
+            os.remove(self.dbpath)
 
-       # Now add records to db
-       for item in xmlAccounts.items.itervalues():
-           self._add_to_db(item)
-       self._db_commit()
+        self._realmName = parser.realm
 
+        self._db_execute("insert into SERVICE (REALM) values (:1)", parser.realm)
+
+        # Now add records to db
+        for item in parser.items.itervalues():
+            self._add_to_db(item)
+        self._db_commit()
+
     def listRecords(self, recordType):
         # Get each account record
         rowiter = self._db_execute("select UID, PSWD, NAME from ACCOUNTS where TYPE = :1", recordType)
@@ -195,7 +211,7 @@
         """
         @return: the collection type assigned to this index.
         """
-        return SQLDirectoryManager.DBTYPE
+        return SQLDirectoryManager.dbType
         
     def _db_init_data_tables(self, q):
         """
@@ -203,6 +219,11 @@
         @param q:           a database cursor to use.
         """
         #
+        # SERVICE table
+        #
+        q.execute("create table SERVICE (REALM text)")
+
+        #
         # ACCOUNTS table
         #
         q.execute(
@@ -245,8 +266,11 @@
     """
     XML based implementation of L{IDirectoryService}.
     """
+    baseGUID = "8256E464-35E0-4DBB-A99C-F0E30C231675"
+    realmName = property(lambda self: self.manager.realmName)
+
     def __repr__(self):
-        return "<%s %r>" % (self.__class__.__name__, self.xmlFile)
+        return "<%s %r: %r>" % (self.__class__.__name__, self.realmName, self.xmlFile)
 
     def __init__(self, dbParentPath, xmlFile = None):
         super(SQLDirectoryService, self).__init__()

Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_apache.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_apache.py	2006-12-08 08:01:19 UTC (rev 760)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_apache.py	2006-12-08 09:43:17 UTC (rev 761)
@@ -52,7 +52,7 @@
     }
 
     def service(self):
-        return self.serviceClass(self.userFile(), self.groupFile())
+        return self.serviceClass(digestRealm, self.userFile(), self.groupFile())
 
     userFileName = None
 
@@ -78,7 +78,7 @@
         """
         IDirectoryService.recordTypes(userFile)
         """
-        self.assertEquals(set(self.serviceClass(self.userFile()).recordTypes()), set(("user",)))
+        self.assertEquals(set(self.serviceClass(digestRealm, self.userFile()).recordTypes()), set(("user",)))
 
     userEntry = None
 

Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_principal.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_principal.py	2006-12-08 08:01:19 UTC (rev 760)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_principal.py	2006-12-08 09:43:17 UTC (rev 761)
@@ -33,7 +33,7 @@
 
 from twistedcaldav.static import CalendarHomeProvisioningFile
 from twistedcaldav.directory.apache import BasicDirectoryService, DigestDirectoryService
-from twistedcaldav.directory.test.test_apache import basicUserFile, digestUserFile, groupFile
+from twistedcaldav.directory.test.test_apache import basicUserFile, digestUserFile, groupFile, digestRealm
 from twistedcaldav.directory.xmlfile import XMLDirectoryService
 from twistedcaldav.directory.test.test_xmlfile import xmlFile
 from twistedcaldav.directory.principal import DirectoryPrincipalProvisioningResource
@@ -43,8 +43,8 @@
 import twistedcaldav.test.util
 
 directoryServices = (
-    BasicDirectoryService(basicUserFile, groupFile),
-    DigestDirectoryService(digestUserFile, groupFile),
+    BasicDirectoryService(digestRealm, basicUserFile, groupFile),
+    DigestDirectoryService(digestRealm, digestUserFile, groupFile),
     XMLDirectoryService(xmlFile),
 )
 
@@ -169,7 +169,7 @@
         DirectoryPrincipalResource.principalUID()
         """
         for provisioningResource, recordType, recordResource, record in self._allRecords():
-            self.assertEquals(record.shortName, recordResource.principalUID())
+            self.assertEquals(record.guid, recordResource.principalUID())
 
     def test_calendarUserAddresses(self):
         """

Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_xmlfile.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_xmlfile.py	2006-12-08 08:01:19 UTC (rev 760)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_xmlfile.py	2006-12-08 09:43:17 UTC (rev 761)
@@ -75,7 +75,7 @@
         self.xmlFile().open("w").write(
 """<?xml version="1.0" encoding="utf-8"?>
 <!DOCTYPE accounts SYSTEM "accounts.dtd">
-<accounts>
+<accounts realm="Test Realm">
   <user>
     <uid>admin</uid>
     <pswd>nimda</pswd>

Modified: CalendarServer/trunk/twistedcaldav/directory/test/util.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/util.py	2006-12-08 08:01:19 UTC (rev 760)
+++ CalendarServer/trunk/twistedcaldav/directory/test/util.py	2006-12-08 09:43:17 UTC (rev 761)
@@ -16,6 +16,8 @@
 # DRI: Wilfredo Sanchez, wsanchez at apple.com
 ##
 
+from urllib import quote
+
 import twisted.trial.unittest
 from twisted.trial.unittest import SkipTest
 from twisted.cred.credentials import UsernamePassword
@@ -46,6 +48,12 @@
         """
         raise NotImplementedError("Subclass needs to implement service()")
 
+    def test_realm(self):
+        """
+        IDirectoryService.realm
+        """
+        self.failUnless(self.service().realmName)
+
     def test_recordTypes(self):
         """
         IDirectoryService.recordTypes()
@@ -196,11 +204,22 @@
             else:
                 return None
 
+        guid = value("guid")
+        if guid is not None:
+            guid = record.guid
+
+        addresses = set(value("addresses"))
+        addresses.add("urn:uuid:%s" % (record.guid,))
+
         self.assertEquals(record.shortName, shortName)
-        self.assertEquals(record.guid, value("guid"))
-        self.assertEquals(set(record.calendarUserAddresses), set(value("addresses")))
-        #self.assertEquals(record.fullName, value("name")) # FIXME
+        self.assertEquals(set(record.calendarUserAddresses), addresses)
 
+        if value("guid"):
+            self.assertEquals(record.guid, value("guid"))
+
+        if value("name"):
+            self.assertEquals(record.fullName, value("name"))
+
 class BasicTestCase (DirectoryTestCase):
     """
     Tests a directory implementation with basic auth.

Modified: CalendarServer/trunk/twistedcaldav/directory/util.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/util.py	2006-12-08 08:01:19 UTC (rev 760)
+++ CalendarServer/trunk/twistedcaldav/directory/util.py	2006-12-08 09:43:17 UTC (rev 761)
@@ -43,6 +43,10 @@
         bytes = chr((namespace >> shift) & 0xff) + bytes
     namespace = bytes
 
+    # We don't want Unicode here; convert to UTF-8
+    if type(name) is unicode:
+        name = name.encode("utf-8")
+
     # Start with a SHA-1 hash of the namespace and name
     uuid = sha(namespace + name).digest()[:16]
 

Modified: CalendarServer/trunk/twistedcaldav/directory/xmlfile.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/xmlfile.py	2006-12-08 08:01:19 UTC (rev 760)
+++ CalendarServer/trunk/twistedcaldav/directory/xmlfile.py	2006-12-08 09:43:17 UTC (rev 761)
@@ -35,8 +35,17 @@
     """
     XML based implementation of L{IDirectoryService}.
     """
+    baseGUID = "9CA8DEC5-5A17-43A9-84A8-BE77C1FB9172"
+
+    def _getRealmName(self):
+        if not hasattr(self, "_realmName"):
+            self._accounts()
+        return self._realmName
+
+    realmName = property(_getRealmName)
+
     def __repr__(self):
-        return "<%s %r>" % (self.__class__.__name__, self.xmlFile)
+        return "<%s %r: %r>" % (self.__class__.__name__, self.realmName, self.xmlFile)
 
     def __init__(self, xmlFile):
         super(XMLDirectoryService, self).__init__()
@@ -82,7 +91,7 @@
         if fileInfo != self._fileInfo:
             parser = XMLAccountsParser(self.xmlFile)
             self._parsedAccounts = parser.items
-            self.realmName = parser.realm
+            self._realmName = parser.realm
             self._fileInfo = fileInfo
         return self._parsedAccounts
 

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


More information about the calendarserver-changes mailing list