[CalendarServer-changes] [1139] CalendarServer/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Tue Feb 6 14:11:51 PST 2007
Revision: 1139
http://trac.macosforge.org/projects/calendarserver/changeset/1139
Author: cdaboo at apple.com
Date: 2007-02-06 14:11:51 -0800 (Tue, 06 Feb 2007)
Log Message:
-----------
Merge branches/users/cdaboo/od-scheme-1044 to trunk.
Modified Paths:
--------------
CalendarServer/trunk/run
CalendarServer/trunk/twistedcaldav/config.py
CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py
CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectory.py
Added Paths:
-----------
CalendarServer/trunk/support/directorysetup.py
CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectoryschema.py
Modified: CalendarServer/trunk/run
===================================================================
--- CalendarServer/trunk/run 2007-02-06 22:10:09 UTC (rev 1138)
+++ CalendarServer/trunk/run 2007-02-06 22:11:51 UTC (rev 1139)
@@ -417,7 +417,7 @@
if ! py_have_module opendirectory; then
opendirectory="${top}/PyOpenDirectory";
- svn_get "PyOpenDirectory" "${opendirectory}" "${svn_uri_base}/PyOpenDirectory/trunk" 1032;
+ svn_get "PyOpenDirectory" "${opendirectory}" "${svn_uri_base}/PyOpenDirectory/trunk" 1138;
py_build "PyOpenDirectory" "${opendirectory}" false;
py_install "PyOpenDirectory" "${opendirectory}";
Copied: CalendarServer/trunk/support/directorysetup.py (from rev 1136, CalendarServer/branches/users/cdaboo/od-schema-1044/support/directorysetup.py)
===================================================================
--- CalendarServer/trunk/support/directorysetup.py (rev 0)
+++ CalendarServer/trunk/support/directorysetup.py 2007-02-06 22:11:51 UTC (rev 1139)
@@ -0,0 +1,304 @@
+#!/usr/bin/env python
+#
+##
+# Copyright (c) 2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# DRI: Cyrus Daboo, cdaboo at apple.com
+##
+#
+# ...
+#
+
+
+import getopt
+import os
+import sys
+
+def usage():
+ print """Usage: directorysetup [options] init|addUser|disableUser|removeUser <<user>>
+Options:
+ -h Print this help and exit
+ -n node OpenDirectory node to target (/LDAPv3/127.0.0.1)
+ -u uid OpenDirectory Admin user id
+ -p pswd OpenDirectory Admin user password
+ -c cname OpenDirectory /Computers record name for the calendar server
+
+Description:
+This is a little command line utility to setup a directory server with records
+conforming to the new schema used by the calendar server. It has several "actions":
+
+"init" - this action will modify the computer record for the host calendar server to
+add the new "com.apple.macosxserver.virtualhosts" entry.
+
+"addUser" - modifies a user record to enable use of the calendar server.
+
+"disableUser" - modifies a user record to disable use of the calendar server.
+
+"removeUser" - modifies a user record to prevent use of the calendar server by
+ removing any reference to the service.
+
+"""
+
+def initComputerRecord(admin_user, admin_pswd, node, recordname):
+ plistdefault = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>ReplicaName</key>
+ <string>Master</string>
+ <key>com.apple.od.role</key>
+ <string>master</string>
+</dict>
+</plist>
+"""
+ plistdefault = plistdefault.replace('"', '\\"')
+ plistdefault = plistdefault.replace('\n', '')
+
+ plist_good = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+ <key>ReplicaName</key>
+ <string>Master</string>
+
+ <key>com.apple.od.role</key>
+ <string>master</string>
+
+ <key>com.apple.macosxserver.virtualhosts</key>
+ <dict>
+ <key>4F088107-51FD-4DE5-904D-2C0AD9C6C893</key>
+ <dict>
+ <key>hostname</key>
+ <string>foo.apple.com</string>
+
+ <key>hostDetails</key>
+ <dict>
+ <key>http</key>
+ <dict>
+ <key>port</key>
+ <integer>80</integer>
+ </dict>
+ <key>https</key>
+ <dict>
+ <key>port</key>
+ <integer>443</integer>
+ </dict>
+ </dict>
+
+ <key>serviceType</key>
+ <array>
+ <string>wiki</string>
+ <string>webCalendar</string>
+ <string>webMailingList</string>
+ </array>
+
+ <key>serviceInfo</key>
+ <dict>
+ <key>webCalendar</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
+ <key>urlMask</key>
+ <string>%(scheme)s://%(hostname)s:%(port)s/groups/%(name)s/webcalendar</string>
+ </dict>
+ <key>wiki</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
+ <key>urlMask</key>
+ <string>%(scheme)s://%(hostname)s:%(port)s/groups/%(name)s/wiki</string>
+ </dict>
+ <key>webMailingList</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
+ <key>urlMask</key>
+ <string>%(scheme)s://%(hostname)s:%(port)s/groups/%(name)s/mailinglist</string>
+ </dict>
+ </dict>
+ </dict>
+
+ <key>C18C34AC-3D9E-403C-8A33-BFC303F3840E</key>
+ <dict>
+ <key>hostname</key>
+ <string>calendar.apple.com</string>
+
+ <key>hostDetails</key>
+ <dict>
+ <key>http</key>
+ <dict>
+ <key>port</key>
+ <integer>8008</integer>
+ </dict>
+ <key>https</key>
+ <dict>
+ <key>port</key>
+ <integer>8443</integer>
+ </dict>
+ </dict>
+
+ <key>serviceType</key>
+ <array>
+ <string>calendar</string>
+ </array>
+
+ <key>serviceInfo</key>
+ <dict>
+ <key>calendar</key>
+ <dict>
+ <key>templates</key>
+ <dict>
+ <key>principalPath</key>
+ <string>/principals/%(type)s/%(name)s</string>
+ <key>calendarUserAddresses</key>
+ <array>
+ <string>%(scheme)s://%(hostname)s:%(port)s/principals/%(type)s/%(name)s</string>
+ <string>mailto:%(email)s</string>
+ <string>urn:uuid:%(guid)s</string>
+ </array>
+ </dict>
+ </dict>
+ </dict>
+ </dict>
+
+ </dict>
+ </dict>
+</plist>
+"""
+
+ plist_good = plist_good.replace('"', '\\"')
+ plist_good = plist_good.replace('\n', '')
+ cmd = "dscl -u %s -P %s %s -create /Computers/%s \"dsAttrTypeStandard:XMLPlist\" \"%s\"" % (admin_user, admin_pswd, node, recordname, plist_good,)
+ print cmd
+ os.system(cmd)
+
+def getComputerRecordGUID(admin_user, admin_pswd, node, computername):
+ # First get the generatedGUID for this computer
+ cmd = "dscl -u %s -P %s %s -read /Computers/%s \"dsAttrTypeStandard:GeneratedUID\"" % (admin_user, admin_pswd, node, computername,)
+ print cmd
+ result = os.popen(cmd, "r")
+ for line in result:
+ return line[len("GeneratedUID: "):-1]
+
+def addUser(admin_user, admin_pswd, node, computername, username):
+
+ uid = getComputerRecordGUID(admin_user, admin_pswd, node, computername)
+ servicetag = "%s:%s:calendar" % (uid, "C18C34AC-3D9E-403C-8A33-BFC303F3840E")
+
+ cmd = "dscl -u %s -P %s %s -create /Users/%s \"dsAttrTypeStandard:ServicesLocator\" \"%s\"" % (admin_user, admin_pswd, node, username, servicetag,)
+ print cmd
+ os.system(cmd)
+
+def disableUser(admin_user, admin_pswd, node, computername, username):
+
+ uid = getComputerRecordGUID(admin_user, admin_pswd, node, computername)
+ servicetag = "%s:%s:calendar:disabled" % (uid, "C18C34AC-3D9E-403C-8A33-BFC303F3840E")
+
+ cmd = "dscl -u %s -P %s %s -create /Users/%s \"dsAttrTypeStandard:ServicesLocator\" \"%s\"" % (admin_user, admin_pswd, node, username, servicetag,)
+ print cmd
+ os.system(cmd)
+
+def removeUser(admin_user, admin_pswd, node, computername, username):
+ cmd = "dscl -u %s -P %s %s -delete /Users/%s \"dsAttrTypeStandard:ServicesLocator\"" % (admin_user, admin_pswd, node, username,)
+ print cmd
+ os.system(cmd)
+
+if __name__ == "__main__":
+
+ try:
+ options, args = getopt.getopt(sys.argv[1:], "hc:n:p:u:")
+
+ node = "/LDAPv3/127.0.0.1"
+ admin_user = None
+ admin_pswd = None
+ computername = None
+
+ for option, value in options:
+ if option == "-h":
+ usage()
+ sys.exit(0)
+ elif option == "-n":
+ node = value
+ elif option == "-u":
+ admin_user = value
+ elif option == "-p":
+ admin_pswd = value
+ elif option == "-c":
+ computername = value
+ else:
+ print "Unrecognized option: %s" % (option,)
+ usage()
+ raise ValueError
+
+ # Some options are required
+ if not admin_user:
+ print "A user name must be specified with the -u option"
+ if not admin_pswd:
+ print "A password must be specified with the -p option"
+ if not computername:
+ print "A computer record name must be specified with the -c option"
+ if not admin_user or not admin_pswd or not computername:
+ usage()
+ raise ValueError
+
+ # Process arguments
+ if len(args) == 0:
+ print "No arguments given. One of 'init', 'addUser', 'disableUser', 'removeUser' must be present."
+ usage()
+ raise ValueError
+ elif args[0] not in ("init", "addUser", "disableUser", "removeUser"):
+ print "Wrong arguments given: %s" % (args[0],)
+ usage()
+ raise ValueError
+
+ if args[0] == "init":
+ if len(args) > 1:
+ print "Too many arguments given to 'init'"
+ usage()
+ raise ValueError
+ initComputerRecord(admin_user, admin_pswd, node, "caldav.apple.com$")
+ elif args[0] == "addUser":
+ if len(args) > 2:
+ print "Too many arguments given to 'addUser'"
+ usage()
+ raise ValueError
+ elif len(args) != 2:
+ print "'addUser' must have one argument - the user name"
+ usage()
+ raise ValueError
+ addUser(admin_user, admin_pswd, node, computername, args[1])
+ elif args[0] == "disableUser":
+ if len(args) > 2:
+ print "Too many arguments given to 'disableUser'"
+ usage()
+ raise ValueError
+ elif len(args) != 2:
+ print "'disableUser' must have one argument - the user name"
+ usage()
+ raise ValueError
+ disableUser(admin_user, admin_pswd, node, computername, args[1])
+ elif args[0] == "removeUser":
+ if len(args) > 2:
+ print "Too many arguments given to 'removeUser'"
+ usage()
+ raise ValueError
+ elif len(args) != 2:
+ print "'removeUser' must have one argument - the user name"
+ usage()
+ raise ValueError
+ removeUser(admin_user, admin_pswd, node, computername, args[1])
+
+ except Exception, e:
+ sys.exit(str(e))
Property changes on: CalendarServer/trunk/support/directorysetup.py
___________________________________________________________________
Name: svn:executable
+ *
Modified: CalendarServer/trunk/twistedcaldav/config.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/config.py 2007-02-06 22:10:09 UTC (rev 1138)
+++ CalendarServer/trunk/twistedcaldav/config.py 2007-02-06 22:11:51 UTC (rev 1139)
@@ -48,6 +48,7 @@
'ServerStatsFile': '/Library/CalendarServer/Documents/stats.plist',
'UserQuotaBytes': 104857600,
'Verbose': False,
+ 'ServerHostName': 'localhost',
'SACLEnable': False,
'Authentication': {
'Basic': {
Modified: CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py 2007-02-06 22:10:09 UTC (rev 1138)
+++ CalendarServer/trunk/twistedcaldav/directory/appleopendirectory.py 2007-02-06 22:11:51 UTC (rev 1139)
@@ -35,9 +35,12 @@
from twisted.internet.reactor import callLater
from twisted.cred.credentials import UsernamePassword
+from twistedcaldav.config import config
from twistedcaldav.directory.directory import DirectoryService, DirectoryRecord
from twistedcaldav.directory.directory import DirectoryError, UnknownRecordTypeError
+from plistlib import readPlistFromString
+
recordListCacheTimeout = 60 * 30 # 30 minutes
class OpenDirectoryService(DirectoryService):
@@ -49,23 +52,36 @@
def __repr__(self):
return "<%s %r: %r>" % (self.__class__.__name__, self.realmName, self.node)
- def __init__(self, node="/Search"):
+ def __init__(self, node="/Search", dosetup=True):
"""
@param node: an OpenDirectory node name to bind to.
+ @param dosetup: if C{True} then the directory records are initialized,
+ if C{False} they are not.
+ This should only be set to C{False} when doing unit tests.
"""
- directory = opendirectory.odInit(node)
- if directory is None:
- raise OpenDirectoryInitError("Failed to open Open Directory Node: %s" % (node,))
+ try:
+ directory = opendirectory.odInit(node)
+ except opendirectory.ODError, e:
+ log.msg("Open Directory (node=%s) Initialization error: %s" % (node, e))
+ raise
self.realmName = node
self.directory = directory
self.node = node
+ self.computerRecordName = ""
self._records = {}
self._delayedCalls = set()
- for recordType in self.recordTypes():
- self.recordsForType(recordType)
+ if dosetup:
+ try:
+ self._lookupVHostRecord()
+ except Exception, e:
+ log.err("Unable to locate virtual host record: %s" % (e,))
+ raise
+ for recordType in self.recordTypes():
+ self.recordsForType(recordType)
+
def __cmp__(self, other):
if not isinstance(other, DirectoryRecord):
return super(DirectoryRecord, self).__eq__(other)
@@ -82,6 +98,229 @@
h = (h + hash(getattr(self, attr))) & sys.maxint
return h
+ def _lookupVHostRecord(self):
+ """
+ Get the OD service record for this host.
+ """
+
+ # The server must have been configured with a virtual hostname.
+ vhostname = config.ServerHostName
+ if not vhostname:
+ raise OpenDirectoryInitError(
+ "There is no virtual hostname configured for the server for use with Open Directory (node=%s)"
+ % (self.realmName,)
+ )
+
+ # Find a record in /Computers with an ENetAddress attribute value equal to the MAC address
+ # and return some useful attributes.
+ attrs = [
+ dsattributes.kDS1AttrGeneratedUID,
+ dsattributes.kDSNAttrRecordName,
+ dsattributes.kDS1AttrXMLPlist,
+ ]
+
+ records = opendirectory.queryRecordsWithAttributes(
+ self.directory,
+ { dsattributes.kDS1AttrXMLPlist: vhostname },
+ dsattributes.eDSContains,
+ True, # case insentive for hostnames
+ False,
+ dsattributes.kDSStdRecordTypeComputers,
+ attrs
+ )
+ self._parseComputersRecords(records, vhostname)
+
+ def _parseComputersRecords(self, records, vhostname):
+
+ # Must have some results
+ if len(records) == 0:
+ raise OpenDirectoryInitError(
+ "Open Directory (node=%s) has no /Computers records with a virtual hostname: %s"
+ % (self.realmName, vhostname,)
+ )
+
+ # Now find a single record that actually matches the hostname
+ found = False
+ for recordname, record in records.iteritems():
+
+ # Must have XMLPlist value
+ plist = record.get(dsattributes.kDS1AttrXMLPlist, None)
+ if not plist:
+ continue
+
+ if not self._parseXMLPlist(vhostname, recordname, plist, record[dsattributes.kDS1AttrGeneratedUID]):
+ continue
+ elif found:
+ raise OpenDirectoryInitError(
+ "Open Directory (node=%s) multiple /Computers records found matching virtual hostname: %s"
+ % (self.realmName, vhostname,)
+ )
+ else:
+ found = True
+
+ if not found:
+ raise OpenDirectoryInitError(
+ "Open Directory (node=%s) no /Computers records with an enabled and valid calendar service were found matching virtual hostname: %s"
+ % (self.realmName, vhostname,)
+ )
+
+ def _parseXMLPlist(self, vhostname, recordname, plist, recordguid):
+ # Parse the plist and look for our special entry
+ plist = readPlistFromString(plist)
+ vhosts = plist.get("com.apple.macosxserver.virtualhosts", None)
+ if not vhosts:
+ log.msg(
+ "Open Directory (node=%s) /Computers/%s record does not have a "
+ "com.apple.macosxserver.virtualhosts in its XMLPlist attribute value"
+ % (self.realmName, recordname)
+ )
+ return False
+
+ # Iterate over each vhost and find one that is a calendar service
+ hostguid = None
+ for key, value in vhosts.iteritems():
+ serviceTypes = value.get("serviceType", None)
+ if serviceTypes:
+ for type in serviceTypes:
+ if type == "calendar":
+ hostguid = key
+ break
+
+ if not hostguid:
+ log.msg(
+ "Open Directory (node=%s) /Computers/%s record does not have a "
+ "calendar service in its XMLPlist attribute value"
+ % (self.realmName, recordname)
+ )
+ return False
+
+ # Get host name
+ hostname = vhosts[hostguid].get("hostname", None)
+ if not hostname:
+ log.msg(
+ "Open Directory (node=%s) /Computers/%s record does not have "
+ "any host name in its XMLPlist attribute value"
+ % (self.realmName, recordname)
+ )
+ return False
+ if hostname != vhostname:
+ log.msg(
+ "Open Directory (node=%s) /Computers/%s record hostname (%s) "
+ "does not match this server (%s)"
+ % (self.realmName, recordname, hostname, vhostname)
+ )
+ return False
+
+ # Get host details and create host templates
+ hostdetails = vhosts[hostguid].get("hostDetails", None)
+ if not hostdetails:
+ log.msg(
+ "Open Directory (node=%s) /Computers/%s record does not have "
+ "any host details in its XMLPlist attribute value"
+ % (self.realmName, recordname)
+ )
+ return False
+ self.hostvariants = []
+ for key, value in hostdetails.iteritems():
+ self.hostvariants.append((key, hostname, value["port"]))
+ self.hostvariants = tuple(self.hostvariants)
+
+ # Look at the service data
+ serviceInfos = vhosts[hostguid].get("serviceInfo", None)
+ if not serviceInfos or not serviceInfos.has_key("calendar"):
+ log.msg(
+ "Open Directory (node=%s) /Computers/%s record does not have a "
+ "calendar service in its XMLPlist attribute value"
+ % (self.realmName, recordname)
+ )
+ return False
+ serviceInfo = serviceInfos["calendar"]
+
+ # Check that this service is enabled
+ enabled = serviceInfo.get("enabled", True)
+ if not enabled:
+ log.msg(
+ "Open Directory (node=%s) /Computers/%s record does not have an "
+ "enabled calendar service in its XMLPlist attribute value"
+ % (self.realmName, recordname)
+ )
+ return False
+
+ # Get useful templates
+ templates = serviceInfo.get("templates", None)
+ if not templates or not templates.has_key("calendarUserAddresses"):
+ log.msg(
+ "Open Directory (node=%s) /Computers/%s record does not have a "
+ "template for calendar user addresses in its XMLPlist attribute value"
+ % (self.realmName, recordname)
+ )
+ return False
+
+ self.computerRecordName = recordname
+
+ # Grab the templates we need for calendar user addresses
+ self.cuaddrtemplates = tuple(templates["calendarUserAddresses"])
+
+ # Create the string we will use to match users with accounts on this server
+ self.servicetag = "%s:%s:calendar" % (recordguid, hostguid)
+
+ return True
+
+ def _templateExpandCalendarUserAddresses(self, recordType, recordName, record):
+ """
+ Expand this services calendar user address templates for the specified record.
+
+ @param recordName: a C{str} containing the record name being operated on.
+ @param record: a C{dict} containing the attributes retrieved from the directory.
+ @return: a C{set} of C{str} for each expanded calendar user address.
+ """
+
+ # Make a dict of the substitutions we can do for this record. The only record parameters
+ # we substitute are name, guid and email. Note that email is multi-valued so we have to
+ # create a list of dicts for each one of those.
+ name = recordName
+ type = recordType
+ guid = record.get(dsattributes.kDS1AttrGeneratedUID)
+ emails = record.get(dsattributes.kDSNAttrEMailAddress)
+ if emails is not None and isinstance(emails, str):
+ emails = [emails]
+
+ subslist = []
+ if emails:
+ for email in emails:
+ subslist.append({
+ "name" : name,
+ "type" : type,
+ "guid" : guid,
+ "email" : email,
+ })
+ else:
+ subslist.append({
+ "name" : name,
+ "type" : type,
+ "guid" : guid,
+ })
+
+ # Now do substitutions
+ result = set()
+ for template in self.cuaddrtemplates:
+ for scheme, hostname, port in self.hostvariants:
+ for subs in subslist:
+ # Add in host substitution values
+ subs.update({
+ "scheme" : scheme,
+ "hostname" : hostname,
+ "port" : port,
+ })
+
+ # Special check for no email address for this record
+ if (template.find("%(email)s") != -1) and not emails:
+ continue
+
+ result.add(template % subs)
+
+ return result
+
def recordTypes(self):
return (
DirectoryService.recordType_users,
@@ -100,10 +339,15 @@
def reloadCache():
log.msg("Reloading %s record cache" % (recordType,))
+ query = {
+ dsattributes.kDSNAttrServicesLocator: self.servicetag,
+ }
+
attrs = [
dsattributes.kDS1AttrGeneratedUID,
dsattributes.kDS1AttrDistinguishedName,
- dsattributes.kDSNAttrCalendarPrincipalURI,
+ dsattributes.kDSNAttrEMailAddress,
+ dsattributes.kDSNAttrServicesLocator,
]
if recordType == DirectoryService.recordType_users:
@@ -116,31 +360,49 @@
elif recordType == DirectoryService.recordType_resources:
listRecordType = dsattributes.kDSStdRecordTypeResources
else:
- raise UnknownRecordTypeError("Unknown Open Directory record type: %s" % (recordType,))
+ raise UnknownRecordTypeError("Unknown Open Directory record type: %s"
+ % (recordType,))
records = {}
try:
- results = opendirectory.listAllRecordsWithAttributes(self.directory, listRecordType, attrs)
+ results = opendirectory.queryRecordsWithAttributes(
+ self.directory,
+ query,
+ dsattributes.eDSStartsWith,
+ False,
+ False,
+ listRecordType,
+ attrs)
except opendirectory.ODError, ex:
- log.msg("OpenDirectory error: %s", str(ex))
+ log.msg("Open Directory (node=%s) error: %s" % (self.realmName, str(ex)))
raise
for (key, value) in results.iteritems():
+ # Make sure this user has service enabled.
+ enabled = True
+ service = value.get(dsattributes.kDSNAttrServicesLocator)
+ if isinstance(service, str):
+ service = [service]
+ for item in service:
+ if item.startswith(self.servicetag):
+ if item.endswith(":disabled"):
+ enabled = False
+ break
+ if not enabled:
+ continue
+
+ # Now get useful record info.
shortName = key
guid = value.get(dsattributes.kDS1AttrGeneratedUID)
if not guid:
continue
realName = value.get(dsattributes.kDS1AttrDistinguishedName)
- cuaddrs = value.get(dsattributes.kDSNAttrCalendarPrincipalURI)
- cuaddrset = set()
- if cuaddrs is not None:
- if isinstance(cuaddrs, str):
- cuaddrset.add(cuaddrs)
- else:
- cuaddrset.update(cuaddrs)
+ # Get calendar user addresses expanded from service record templates.
+ cuaddrset = self._templateExpandCalendarUserAddresses(recordType, key, value)
+ # Special case for groups.
if recordType == DirectoryService.recordType_groups:
memberGUIDs = value.get(dsattributes.kDSNAttrGroupMembers)
if memberGUIDs is None:
@@ -238,7 +500,8 @@
try:
return opendirectory.authenticateUserBasic(self.service.directory, self.shortName, credentials.password)
except opendirectory.ODError, e:
- log.err("OpenDirectory error while performing basic authentication for user %s: %r" % (self.shortName, e))
+ log.err("Open Directory (node=%s) error while performing basic authentication for user %s: %r"
+ % (self.service.realmName, self.shortName, e))
return False
return super(OpenDirectoryRecord, self).verifyCredentials(credentials)
Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectory.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectory.py 2007-02-06 22:10:09 UTC (rev 1138)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectory.py 2007-02-06 22:11:51 UTC (rev 1139)
@@ -50,7 +50,7 @@
def setUp(self):
super(OpenDirectory, self).setUp()
- self._service = OpenDirectoryService(node="/Search")
+ self._service = OpenDirectoryService(node="/Search", dosetup=False)
def tearDown(self):
for call in self._service._delayedCalls:
Copied: CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectoryschema.py (from rev 1136, CalendarServer/branches/users/cdaboo/od-schema-1044/twistedcaldav/directory/test/test_opendirectoryschema.py)
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectoryschema.py (rev 0)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_opendirectoryschema.py 2007-02-06 22:11:51 UTC (rev 1139)
@@ -0,0 +1,1018 @@
+##
+# Copyright (c) 2005-2007 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+# DRI: Wilfredo Sanchez, wsanchez at apple.com
+##
+
+try:
+ from twistedcaldav.directory.appleopendirectory import OpenDirectoryService
+ from twistedcaldav.directory.appleopendirectory import OpenDirectoryInitError
+ import dsattributes
+except ImportError:
+ pass
+else:
+ from twistedcaldav.directory.directory import DirectoryService
+ import twisted.trial.unittest
+
+ class PlistParse (twisted.trial.unittest.TestCase):
+ """
+ Test Open Directory service schema.
+ """
+
+ plist_nomacosxserver_key = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+ <key>ReplicaName</key>
+ <string>Master</string>
+
+ <key>com.apple.od.role</key>
+ <string>master</string>
+ </dict>
+</plist>
+"""
+
+ plist_nocalendarservice = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+ <key>ReplicaName</key>
+ <string>Master</string>
+
+ <key>com.apple.od.role</key>
+ <string>master</string>
+
+ <key>com.apple.macosxserver.virtualhosts</key>
+ <dict>
+ <key>4F088107-51FD-4DE5-904D-2C0AD9C6C893</key>
+ <dict>
+ <key>hostname</key>
+ <string>foo.apple.com</string>
+
+ <key>hostDetails</key>
+ <dict>
+ <key>http</key>
+ <dict>
+ <key>port</key>
+ <integer>80</integer>
+ </dict>
+ <key>https</key>
+ <dict>
+ <key>port</key>
+ <integer>443</integer>
+ </dict>
+ </dict>
+
+ <key>serviceType</key>
+ <array>
+ <string>wiki</string>
+ <string>webCalendar</string>
+ <string>webMailingList</string>
+ </array>
+
+ <key>serviceInfo</key>
+ <dict>
+ <key>webCalendar</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
+ <key>urlMask</key>
+ <string>%(scheme)s://%(hostname)s:%(port)s/groups/%(name)s/webcalendar</string>
+ </dict>
+ <key>wiki</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
+ <key>urlMask</key>
+ <string>%(scheme)s://%(hostname)s:%(port)s/groups/%(name)s/wiki</string>
+ </dict>
+ <key>webMailingList</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
+ <key>urlMask</key>
+ <string>%(scheme)s://%(hostname)s:%(port)s/groups/%(name)s/mailinglist</string>
+ </dict>
+ </dict>
+ </dict>
+
+ </dict>
+ </dict>
+</plist>
+"""
+
+ plist_noserviceinfo = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+ <key>ReplicaName</key>
+ <string>Master</string>
+
+ <key>com.apple.od.role</key>
+ <string>master</string>
+
+ <key>com.apple.macosxserver.virtualhosts</key>
+ <dict>
+ <key>4F088107-51FD-4DE5-904D-2C0AD9C6C893</key>
+ <dict>
+ <key>hostname</key>
+ <string>foo.apple.com</string>
+
+ <key>hostDetails</key>
+ <dict>
+ <key>http</key>
+ <dict>
+ <key>port</key>
+ <integer>80</integer>
+ </dict>
+ <key>https</key>
+ <dict>
+ <key>port</key>
+ <integer>443</integer>
+ </dict>
+ </dict>
+
+ <key>serviceType</key>
+ <array>
+ <string>wiki</string>
+ <string>webCalendar</string>
+ <string>webMailingList</string>
+ </array>
+
+ <key>serviceInfo</key>
+ <dict>
+ <key>webCalendar</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
+ <key>urlMask</key>
+ <string>%(scheme)s://%(hostname)s:%(port)s/groups/%(name)s/webcalendar</string>
+ </dict>
+ <key>wiki</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
+ <key>urlMask</key>
+ <string>%(scheme)s://%(hostname)s:%(port)s/groups/%(name)s/wiki</string>
+ </dict>
+ <key>webMailingList</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
+ <key>urlMask</key>
+ <string>%(scheme)s://%(hostname)s:%(port)s/groups/%(name)s/mailinglist</string>
+ </dict>
+ </dict>
+ </dict>
+
+ <key>C18C34AC-3D9E-403C-8A33-BFC303F3840E</key>
+ <dict>
+ <key>hostname</key>
+ <string>calendar.apple.com</string>
+
+ <key>hostDetails</key>
+ <dict>
+ <key>http</key>
+ <dict>
+ <key>port</key>
+ <integer>8008</integer>
+ </dict>
+ <key>https</key>
+ <dict>
+ <key>port</key>
+ <integer>8443</integer>
+ </dict>
+ </dict>
+
+ <key>serviceType</key>
+ <array>
+ <string>calendar</string>
+ </array>
+
+ <key>serviceInfo</key>
+ <dict>
+ <key>webCalendar</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
+ <key>urlMask</key>
+ <string>%(scheme)s://%(hostname)s:%(port)s/groups/%(name)s/webcalendar</string>
+ </dict>
+ </dict>
+ </dict>
+
+ </dict>
+ </dict>
+</plist>
+"""
+
+ plist_disabledservice = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+ <key>ReplicaName</key>
+ <string>Master</string>
+
+ <key>com.apple.od.role</key>
+ <string>master</string>
+
+ <key>com.apple.macosxserver.virtualhosts</key>
+ <dict>
+ <key>4F088107-51FD-4DE5-904D-2C0AD9C6C893</key>
+ <dict>
+ <key>hostname</key>
+ <string>foo.apple.com</string>
+
+ <key>hostDetails</key>
+ <dict>
+ <key>http</key>
+ <dict>
+ <key>port</key>
+ <integer>80</integer>
+ </dict>
+ <key>https</key>
+ <dict>
+ <key>port</key>
+ <string>443</string>
+ </dict>
+ </dict>
+
+ <key>serviceType</key>
+ <array>
+ <string>wiki</string>
+ <string>webCalendar</string>
+ <string>webMailingList</string>
+ </array>
+
+ <key>serviceInfo</key>
+ <dict>
+ <key>webCalendar</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
+ <key>urlMask</key>
+ <string>%(scheme)s://%(hostname)s:%(port)s/groups/%(name)s/webcalendar</string>
+ </dict>
+ <key>wiki</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
+ <key>urlMask</key>
+ <string>%(scheme)s://%(hostname)s:%(port)s/groups/%(name)s/wiki</string>
+ </dict>
+ <key>webMailingList</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
+ <key>urlMask</key>
+ <string>%(scheme)s://%(hostname)s:%(port)s/groups/%(name)s/mailinglist</string>
+ </dict>
+ </dict>
+ </dict>
+
+ <key>C18C34AC-3D9E-403C-8A33-BFC303F3840E</key>
+ <dict>
+ <key>hostname</key>
+ <string>calendar.apple.com</string>
+
+ <key>hostDetails</key>
+ <dict>
+ <key>http</key>
+ <dict>
+ <key>port</key>
+ <integer>8008</integer>
+ </dict>
+ <key>https</key>
+ <dict>
+ <key>port</key>
+ <integer>8443</integer>
+ </dict>
+ </dict>
+
+ <key>serviceType</key>
+ <array>
+ <string>calendar</string>
+ </array>
+
+ <key>serviceInfo</key>
+ <dict>
+ <key>calendar</key>
+ <dict>
+ <key>enabled</key>
+ <false/>
+ <key>templates</key>
+ <dict>
+ <key>principalPath</key>
+ <string>/principals/%(type)s/%(name)s</string>
+ <key>calendarUserAddresses</key>
+ <array>
+ <string>%(scheme)s://%(hostname)s:%(port)s/principals/%(type)s/%(name)s</string>
+ <string>mailto:%(email)s</string>
+ <string>urn:uuid:%(guid)s</string>
+ </array>
+ </dict>
+ </dict>
+ </dict>
+ </dict>
+
+ </dict>
+ </dict>
+</plist>
+"""
+
+ plist_nohostname = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+ <key>ReplicaName</key>
+ <string>Master</string>
+
+ <key>com.apple.od.role</key>
+ <string>master</string>
+
+ <key>com.apple.macosxserver.virtualhosts</key>
+ <dict>
+ <key>4F088107-51FD-4DE5-904D-2C0AD9C6C893</key>
+ <dict>
+ <key>hostname</key>
+ <string>foo.apple.com</string>
+
+ <key>hostDetails</key>
+ <dict>
+ <key>http</key>
+ <dict>
+ <key>port</key>
+ <integer>80</integer>
+ </dict>
+ <key>https</key>
+ <dict>
+ <key>port</key>
+ <string>443</string>
+ </dict>
+ </dict>
+
+ <key>serviceType</key>
+ <array>
+ <string>wiki</string>
+ <string>webCalendar</string>
+ <string>webMailingList</string>
+ </array>
+
+ <key>serviceInfo</key>
+ <dict>
+ <key>webCalendar</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
+ <key>urlMask</key>
+ <string>%(scheme)s://%(hostname)s:%(port)s/groups/%(name)s/webcalendar</string>
+ </dict>
+ <key>wiki</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
+ <key>urlMask</key>
+ <string>%(scheme)s://%(hostname)s:%(port)s/groups/%(name)s/wiki</string>
+ </dict>
+ <key>webMailingList</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
+ <key>urlMask</key>
+ <string>%(scheme)s://%(hostname)s:%(port)s/groups/%(name)s/mailinglist</string>
+ </dict>
+ </dict>
+ </dict>
+
+ <key>C18C34AC-3D9E-403C-8A33-BFC303F3840E</key>
+ <dict>
+ <key>hostDetails</key>
+ <dict>
+ <key>http</key>
+ <dict>
+ <key>port</key>
+ <integer>8008</integer>
+ </dict>
+ <key>https</key>
+ <dict>
+ <key>port</key>
+ <integer>8443</integer>
+ </dict>
+ </dict>
+
+ <key>serviceType</key>
+ <array>
+ <string>calendar</string>
+ </array>
+
+ <key>serviceInfo</key>
+ <dict>
+ <key>calendar</key>
+ <dict>
+ <key>templates</key>
+ <dict>
+ <key>principalPath</key>
+ <string>/principals/%(type)s/%(name)s</string>
+ <key>calendarUserAddresses</key>
+ <array>
+ <string>%(scheme)s://%(hostname)s:%(port)s/principals/%(type)s/%(name)s</string>
+ <string>mailto:%(email)s</string>
+ <string>urn:uuid:%(guid)s</string>
+ </array>
+ </dict>
+ </dict>
+ </dict>
+ </dict>
+
+ </dict>
+ </dict>
+</plist>
+"""
+
+ plist_nohostdetails = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+ <key>ReplicaName</key>
+ <string>Master</string>
+
+ <key>com.apple.od.role</key>
+ <string>master</string>
+
+ <key>com.apple.macosxserver.virtualhosts</key>
+ <dict>
+ <key>4F088107-51FD-4DE5-904D-2C0AD9C6C893</key>
+ <dict>
+ <key>hostname</key>
+ <string>foo.apple.com</string>
+
+ <key>hostDetails</key>
+ <dict>
+ <key>http</key>
+ <dict>
+ <key>port</key>
+ <integer>80</integer>
+ </dict>
+ <key>https</key>
+ <dict>
+ <key>port</key>
+ <string>443</string>
+ </dict>
+ </dict>
+
+ <key>serviceType</key>
+ <array>
+ <string>wiki</string>
+ <string>webCalendar</string>
+ <string>webMailingList</string>
+ </array>
+
+ <key>serviceInfo</key>
+ <dict>
+ <key>webCalendar</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
+ <key>urlMask</key>
+ <string>%(scheme)s://%(hostname)s:%(port)s/groups/%(name)s/webcalendar</string>
+ </dict>
+ <key>wiki</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
+ <key>urlMask</key>
+ <string>%(scheme)s://%(hostname)s:%(port)s/groups/%(name)s/wiki</string>
+ </dict>
+ <key>webMailingList</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
+ <key>urlMask</key>
+ <string>%(scheme)s://%(hostname)s:%(port)s/groups/%(name)s/mailinglist</string>
+ </dict>
+ </dict>
+ </dict>
+
+ <key>C18C34AC-3D9E-403C-8A33-BFC303F3840E</key>
+ <dict>
+ <key>hostname</key>
+ <string>calendar.apple.com</string>
+
+ <key>serviceType</key>
+ <array>
+ <string>calendar</string>
+ </array>
+
+ <key>serviceInfo</key>
+ <dict>
+ <key>calendar</key>
+ <dict>
+ <key>templates</key>
+ <dict>
+ <key>principalPath</key>
+ <string>/principals/%(type)s/%(name)s</string>
+ <key>calendarUserAddresses</key>
+ <array>
+ <string>%(scheme)s://%(hostname)s:%(port)s/principals/%(type)s/%(name)s</string>
+ <string>mailto:%(email)s</string>
+ <string>urn:uuid:%(guid)s</string>
+ </array>
+ </dict>
+ </dict>
+ </dict>
+ </dict>
+
+ </dict>
+ </dict>
+</plist>
+"""
+
+ plist_nocuaddrtemplates = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+ <key>ReplicaName</key>
+ <string>Master</string>
+
+ <key>com.apple.od.role</key>
+ <string>master</string>
+
+ <key>com.apple.macosxserver.virtualhosts</key>
+ <dict>
+ <key>4F088107-51FD-4DE5-904D-2C0AD9C6C893</key>
+ <dict>
+ <key>hostname</key>
+ <string>foo.apple.com</string>
+
+ <key>hostDetails</key>
+ <dict>
+ <key>http</key>
+ <dict>
+ <key>port</key>
+ <integer>80</integer>
+ </dict>
+ <key>https</key>
+ <dict>
+ <key>port</key>
+ <string>443</string>
+ </dict>
+ </dict>
+
+ <key>serviceType</key>
+ <array>
+ <string>wiki</string>
+ <string>webCalendar</string>
+ <string>webMailingList</string>
+ </array>
+
+ <key>serviceInfo</key>
+ <dict>
+ <key>webCalendar</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
+ <key>urlMask</key>
+ <string>%(scheme)s://%(hostname)s:%(port)s/groups/%(name)s/webcalendar</string>
+ </dict>
+ <key>wiki</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
+ <key>urlMask</key>
+ <string>%(scheme)s://%(hostname)s:%(port)s/groups/%(name)s/wiki</string>
+ </dict>
+ <key>webMailingList</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
+ <key>urlMask</key>
+ <string>%(scheme)s://%(hostname)s:%(port)s/groups/%(name)s/mailinglist</string>
+ </dict>
+ </dict>
+ </dict>
+
+ <key>C18C34AC-3D9E-403C-8A33-BFC303F3840E</key>
+ <dict>
+ <key>hostname</key>
+ <string>calendar.apple.com</string>
+
+ <key>hostDetails</key>
+ <dict>
+ <key>http</key>
+ <dict>
+ <key>port</key>
+ <integer>8008</integer>
+ </dict>
+ <key>https</key>
+ <dict>
+ <key>port</key>
+ <integer>8443</integer>
+ </dict>
+ </dict>
+
+ <key>serviceType</key>
+ <array>
+ <string>calendar</string>
+ </array>
+
+ <key>serviceInfo</key>
+ <dict>
+ <key>calendar</key>
+ <dict>
+ <key>templates</key>
+ <dict>
+ <key>principalPath</key>
+ <string>/principals/%(type)s/%(name)s</string>
+ </dict>
+ </dict>
+ </dict>
+ </dict>
+
+ </dict>
+ </dict>
+</plist>
+"""
+ plist_good = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+ <key>ReplicaName</key>
+ <string>Master</string>
+
+ <key>com.apple.od.role</key>
+ <string>master</string>
+
+ <key>com.apple.macosxserver.virtualhosts</key>
+ <dict>
+ <key>4F088107-51FD-4DE5-904D-2C0AD9C6C893</key>
+ <dict>
+ <key>hostname</key>
+ <string>foo.apple.com</string>
+
+ <key>hostDetails</key>
+ <dict>
+ <key>http</key>
+ <dict>
+ <key>port</key>
+ <integer>80</integer>
+ </dict>
+ <key>https</key>
+ <dict>
+ <key>port</key>
+ <string>443</string>
+ </dict>
+ </dict>
+
+ <key>serviceType</key>
+ <array>
+ <string>wiki</string>
+ <string>webCalendar</string>
+ <string>webMailingList</string>
+ </array>
+
+ <key>serviceInfo</key>
+ <dict>
+ <key>webCalendar</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
+ <key>urlMask</key>
+ <string>%(scheme)s://%(hostname)s:%(port)s/groups/%(name)s/webcalendar</string>
+ </dict>
+ <key>wiki</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
+ <key>urlMask</key>
+ <string>%(scheme)s://%(hostname)s:%(port)s/groups/%(name)s/wiki</string>
+ </dict>
+ <key>webMailingList</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
+ <key>urlMask</key>
+ <string>%(scheme)s://%(hostname)s:%(port)s/groups/%(name)s/mailinglist</string>
+ </dict>
+ </dict>
+ </dict>
+
+ <key>C18C34AC-3D9E-403C-8A33-BFC303F3840E</key>
+ <dict>
+ <key>hostname</key>
+ <string>calendar.apple.com</string>
+
+ <key>hostDetails</key>
+ <dict>
+ <key>http</key>
+ <dict>
+ <key>port</key>
+ <integer>8008</integer>
+ </dict>
+ <key>https</key>
+ <dict>
+ <key>port</key>
+ <integer>8443</integer>
+ </dict>
+ </dict>
+
+ <key>serviceType</key>
+ <array>
+ <string>calendar</string>
+ </array>
+
+ <key>serviceInfo</key>
+ <dict>
+ <key>calendar</key>
+ <dict>
+ <key>templates</key>
+ <dict>
+ <key>principalPath</key>
+ <string>/principals/%(type)s/%(name)s</string>
+ <key>calendarUserAddresses</key>
+ <array>
+ <string>%(scheme)s://%(hostname)s:%(port)s/principals/%(type)s/%(name)s</string>
+ <string>mailto:%(email)s</string>
+ <string>urn:uuid:%(guid)s</string>
+ </array>
+ </dict>
+ </dict>
+ </dict>
+ </dict>
+
+ </dict>
+ </dict>
+</plist>
+"""
+
+ plist_good_other = """<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+ <key>ReplicaName</key>
+ <string>Master</string>
+
+ <key>com.apple.od.role</key>
+ <string>master</string>
+
+ <key>com.apple.macosxserver.virtualhosts</key>
+ <dict>
+ <key>4F088107-51FD-4DE5-904D-2C0AD9C6C893</key>
+ <dict>
+ <key>hostname</key>
+ <string>foo.apple.com</string>
+
+ <key>hostDetails</key>
+ <dict>
+ <key>http</key>
+ <dict>
+ <key>port</key>
+ <integer>80</integer>
+ </dict>
+ <key>https</key>
+ <dict>
+ <key>port</key>
+ <string>443</string>
+ </dict>
+ </dict>
+
+ <key>serviceType</key>
+ <array>
+ <string>wiki</string>
+ <string>webCalendar</string>
+ <string>webMailingList</string>
+ </array>
+
+ <key>serviceInfo</key>
+ <dict>
+ <key>webCalendar</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
+ <key>urlMask</key>
+ <string>%(scheme)s://%(hostname)s:%(port)s/groups/%(name)s/webcalendar</string>
+ </dict>
+ <key>wiki</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
+ <key>urlMask</key>
+ <string>%(scheme)s://%(hostname)s:%(port)s/groups/%(name)s/wiki</string>
+ </dict>
+ <key>webMailingList</key>
+ <dict>
+ <key>enabled</key>
+ <true/>
+ <key>urlMask</key>
+ <string>%(scheme)s://%(hostname)s:%(port)s/groups/%(name)s/mailinglist</string>
+ </dict>
+ </dict>
+ </dict>
+
+ <key>C18C34AC-3D9E-403C-8A33-BFC303F3840E</key>
+ <dict>
+ <key>hostname</key>
+ <string>privatecalendar.apple.com</string>
+
+ <key>hostDetails</key>
+ <dict>
+ <key>http</key>
+ <dict>
+ <key>port</key>
+ <integer>8008</integer>
+ </dict>
+ <key>https</key>
+ <dict>
+ <key>port</key>
+ <integer>8443</integer>
+ </dict>
+ </dict>
+
+ <key>serviceType</key>
+ <array>
+ <string>calendar</string>
+ </array>
+
+ <key>serviceInfo</key>
+ <dict>
+ <key>calendar</key>
+ <dict>
+ <key>templates</key>
+ <dict>
+ <key>principalPath</key>
+ <string>/principals/%(type)s/%(name)s</string>
+ <key>calendarUserAddresses</key>
+ <array>
+ <string>%(scheme)s://%(hostname)s:%(port)s/principals/%(type)s/%(name)s</string>
+ <string>mailto:%(email)s</string>
+ <string>urn:uuid:%(guid)s</string>
+ </array>
+ </dict>
+ </dict>
+ </dict>
+ </dict>
+
+ </dict>
+ </dict>
+</plist>
+"""
+
+ def test_plist_errors(self):
+ def _doParse(plist, title):
+ service = OpenDirectoryService(node="/Search", dosetup=False)
+ if service._parseXMLPlist("calendar.apple.com", "recordit", plist, "GUIDIFY"):
+ self.fail(msg="Plist parse should have failed: %s" % (title,))
+
+ plists = (
+ (PlistParse.plist_nomacosxserver_key, "nomacosxserver_key"),
+ (PlistParse.plist_nocalendarservice, "nocalendarservice"),
+ (PlistParse.plist_noserviceinfo, "noserviceinfo"),
+ (PlistParse.plist_disabledservice, "disabledservice"),
+ (PlistParse.plist_nohostname, "nohostname"),
+ (PlistParse.plist_nohostdetails, "nohostdetails"),
+ (PlistParse.plist_nocuaddrtemplates, "nocuaddrtemplates"),
+ )
+ for plist, title in plists:
+ _doParse(plist, title)
+
+ def test_goodplist(self):
+ service = OpenDirectoryService(node="/Search", dosetup=False)
+ if not service._parseXMLPlist("calendar.apple.com", "recordit", PlistParse.plist_good, "GUIDIFY"):
+ self.fail(msg="Plist parse should not have failed")
+ else:
+ # Verify that we extracted the proper items
+ self.assertEqual(service.servicetag, "GUIDIFY:C18C34AC-3D9E-403C-8A33-BFC303F3840E:calendar")
+ self.assertEqual(service.hostvariants, (("http", "calendar.apple.com", 8008), ("https", "calendar.apple.com", 8443)))
+ self.assertEqual(service.cuaddrtemplates, ("%(scheme)s://%(hostname)s:%(port)s/principals/%(type)s/%(name)s", "mailto:%(email)s", "urn:uuid:%(guid)s"))
+
+ def test_expandcuaddrs(self):
+ def _doTest(recordName, record, result, title):
+ service = OpenDirectoryService(node="/Search", dosetup=False)
+ if not service._parseXMLPlist("calendar.apple.com", recordName, PlistParse.plist_good, "GUIDIFY"):
+ self.fail(msg="Plist parse should not have failed: %s" % (recordName,))
+ else:
+ expanded = service._templateExpandCalendarUserAddresses(DirectoryService.recordType_users, recordName, record)
+
+ # Verify that we extracted the proper items
+ self.assertEqual(expanded, result, msg=title % (expanded, result,))
+
+ data = (
+ (
+ "user01",
+ {
+ dsattributes.kDS1AttrGeneratedUID: "GUID-USER-01",
+ dsattributes.kDSNAttrEMailAddress: "user01 at example.com",
+ },
+ set((
+ "http://calendar.apple.com:8008/principals/users/user01",
+ "https://calendar.apple.com:8443/principals/users/user01",
+ "mailto:user01 at example.com",
+ "urn:uuid:GUID-USER-01",
+ )),
+ "User with one email address, %s != %s",
+ ),
+ (
+ "user02",
+ {
+ dsattributes.kDS1AttrGeneratedUID: "GUID-USER-02",
+ dsattributes.kDSNAttrEMailAddress: ["user02 at example.com", "user02 at calendar.example.com"],
+ },
+ set((
+ "http://calendar.apple.com:8008/principals/users/user02",
+ "https://calendar.apple.com:8443/principals/users/user02",
+ "mailto:user02 at example.com",
+ "mailto:user02 at calendar.example.com",
+ "urn:uuid:GUID-USER-02",
+ )),
+ "User with multiple email addresses, %s != %s",
+ ),
+ (
+ "user03",
+ {
+ dsattributes.kDS1AttrGeneratedUID: "GUID-USER-03",
+ },
+ set((
+ "http://calendar.apple.com:8008/principals/users/user03",
+ "https://calendar.apple.com:8443/principals/users/user03",
+ "urn:uuid:GUID-USER-03",
+ )),
+ "User with no email addresses, %s != %s",
+ ),
+ )
+
+ for recordName, record, result, title in data:
+ _doTest(recordName, record, result, title)
+
+ class ODRecordsParse (twisted.trial.unittest.TestCase):
+
+ record_good = ("computer1.apple.com", {
+ dsattributes.kDS1AttrGeneratedUID : "GUID1",
+ dsattributes.kDSNAttrRecordName : "computer1.apple.com",
+ dsattributes.kDS1AttrXMLPlist : PlistParse.plist_good,
+ })
+ record_good_other = ("computer2.apple.com", {
+ dsattributes.kDS1AttrGeneratedUID : "GUID1",
+ dsattributes.kDSNAttrRecordName : "computer2.apple.com",
+ dsattributes.kDS1AttrXMLPlist : PlistParse.plist_good_other,
+ })
+ record_good_duplicate = ("computer2.apple.com", {
+ dsattributes.kDS1AttrGeneratedUID : "GUID1",
+ dsattributes.kDSNAttrRecordName : "computer2.apple.com",
+ dsattributes.kDS1AttrXMLPlist : PlistParse.plist_good,
+ })
+
+ def test_odrecords_error(self):
+ def _doParseRecords(recordlist, title):
+ service = OpenDirectoryService(node="/Search", dosetup=False)
+ try:
+ service._parseComputersRecords(recordlist, "calendar.apple.com")
+ self.fail(msg="Record parse should have failed: %s" % (title,))
+ except OpenDirectoryInitError:
+ pass
+
+ records = (
+ ({}, "no records found"),
+ ({
+ ODRecordsParse.record_good[0] : ODRecordsParse.record_good[1],
+ ODRecordsParse.record_good_duplicate[0] : ODRecordsParse.record_good_duplicate[1],
+ }, "duplicate records found"),
+ ({
+ ODRecordsParse.record_good_other[0] : ODRecordsParse.record_good_other[1],
+ }, "non-matching record found"),
+ )
+
+ for recordlist, title in records:
+ _doParseRecords(recordlist, title)
+
+ def test_odrecords_good(self):
+ def _doParseRecords(recordlist, title):
+ service = OpenDirectoryService(node="/Search", dosetup=False)
+ try:
+ service._parseComputersRecords(recordlist, "calendar.apple.com")
+ except OpenDirectoryInitError, ex:
+ self.fail(msg="Record parse should not have failed: \"%s\" with error: %s" % (title, ex))
+
+ records = (
+ ({
+ ODRecordsParse.record_good[0] : ODRecordsParse.record_good[1],
+ }, "single good plist"),
+ ({
+ ODRecordsParse.record_good[0] : ODRecordsParse.record_good[1],
+ ODRecordsParse.record_good_other[0] : ODRecordsParse.record_good_other[1],
+ }, "multiple plists"),
+ )
+
+ for recordlist, title in records:
+ _doParseRecords(recordlist, title)
+
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20070206/2d84096f/attachment.html
More information about the calendarserver-changes
mailing list