[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