[CalendarServer-changes] [5044] CalendarServer/branches/users/sagen/locations-resources/ twistedcaldav/directory
source_changes at macosforge.org
source_changes at macosforge.org
Thu Feb 4 19:31:43 PST 2010
Revision: 5044
http://trac.macosforge.org/projects/calendarserver/changeset/5044
Author: sagen at apple.com
Date: 2010-02-04 19:31:39 -0800 (Thu, 04 Feb 2010)
Log Message:
-----------
Adds support for creating/updating/destroying records in a directory service
Modified Paths:
--------------
CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/aggregate.py
CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/cachingdirectory.py
CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/directory.py
CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/xmlfile.py
Added Paths:
-----------
CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/test/modify/
CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/test/modify/augments.xml
CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/test/modify/caldavd.plist
CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/test/modify/resources-locations.xml
CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/test/modify/users-groups.xml
CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/test/test_modify.py
Modified: CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/aggregate.py
===================================================================
--- CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/aggregate.py 2010-02-04 19:52:33 UTC (rev 5043)
+++ CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/aggregate.py 2010-02-05 03:31:39 UTC (rev 5044)
@@ -168,6 +168,29 @@
results.append(result)
return results
+ def createRecord(self, recordType, guid, shortNames=(), authIDs=set(),
+ fullName=None, firstName=None, lastName=None, emailAddresses=set(),
+ uid=None, password=None, **kwds):
+ service = self.serviceForRecordType(recordType)
+ return service.createRecord(recordType, guid, shortNames=shortNames,
+ authIDs=authIDs, fullName=fullName, firstName=firstName,
+ lastName=lastName, emailAddresses=emailAddresses, uid=uid,
+ password=password, **kwds)
+
+ def updateRecord(self, recordType, guid, shortNames=(), authIDs=set(),
+ fullName=None, firstName=None, lastName=None, emailAddresses=set(),
+ uid=None, password=None, **kwds):
+ service = self.serviceForRecordType(recordType)
+ return service.updateRecord(recordType, guid, shortNames=shortNames,
+ authIDs=authIDs, fullName=fullName, firstName=firstName,
+ lastName=lastName, emailAddresses=emailAddresses, uid=uid,
+ password=password, **kwds)
+
+
+ def destroyRecord(self, recordType, guid):
+ service = self.serviceForRecordType(recordType)
+ return service.destroyRecord(recordType, guid)
+
class DuplicateRecordTypeError(DirectoryError):
"""
Duplicate record type.
Modified: CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/cachingdirectory.py
===================================================================
--- CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/cachingdirectory.py 2010-02-04 19:52:33 UTC (rev 5043)
+++ CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/cachingdirectory.py 2010-02-05 03:31:39 UTC (rev 5044)
@@ -150,6 +150,7 @@
self.cacheTimeout = cacheTimeout * 60
+ self.cacheClass = cacheClass
self._initCaches(cacheClass)
super(CachingDirectoryService, self).__init__()
Modified: CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/directory.py
===================================================================
--- CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/directory.py 2010-02-04 19:52:33 UTC (rev 5043)
+++ CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/directory.py 2010-02-05 03:31:39 UTC (rev 5044)
@@ -282,6 +282,29 @@
return result
+ def createRecord(self, recordType, guid, shortNames=(), authIDs=set(),
+ fullName=None, firstName=None, lastName=None, emailAddresses=set(),
+ uid=None, password=None, **kwds):
+ """
+ Create/persist a directory record based on the given values
+ """
+ raise NotImplementedError("Subclass must implement createRecord")
+
+ def updateRecord(self, recordType, guid, shortNames=(), authIDs=set(),
+ fullName=None, firstName=None, lastName=None, emailAddresses=set(),
+ uid=None, password=None, **kwds):
+ """
+ Update/persist a directory record based on the given values
+ """
+ raise NotImplementedError("Subclass must implement updateRecord")
+
+ def destroyRecord(self, recordType, guid):
+ """
+ Remove a directory record from the directory
+ """
+ raise NotImplementedError("Subclass must implement destroyRecord")
+
+
class DirectoryRecord(LoggingMixIn):
implements(IDirectoryRecord)
Added: CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/test/modify/augments.xml
===================================================================
--- CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/test/modify/augments.xml (rev 0)
+++ CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/test/modify/augments.xml 2010-02-05 03:31:39 UTC (rev 5044)
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2009-2010 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.
+ -->
+
+<!DOCTYPE accounts SYSTEM "../../../conf/auth/augments.dtd">
+
+<augments>
+ <record>
+ <guid>user01</guid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ </record>
+ <record>
+ <guid>user02</guid>
+ <enable>false</enable>
+ <enable-calendar>false</enable-calendar>
+ </record>
+</augments>
Added: CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/test/modify/caldavd.plist
===================================================================
--- CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/test/modify/caldavd.plist (rev 0)
+++ CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/test/modify/caldavd.plist 2010-02-05 03:31:39 UTC (rev 5044)
@@ -0,0 +1,748 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Copyright (c) 2006-2010 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.
+ -->
+
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+ <dict>
+
+ <!--
+ Public network address information
+
+ This is the server's public network address, which is provided to
+ clients in URLs and the like. It may or may not be the network
+ address that the server is listening to directly, though it is by
+ default. For example, it may be the address of a load balancer or
+ proxy which forwards connections to the server.
+ -->
+
+ <!-- Network host name [empty = system host name] -->
+ <key>ServerHostName</key>
+ <string></string> <!-- The hostname clients use when connecting -->
+
+ <!-- HTTP port [0 = disable HTTP] -->
+ <key>HTTPPort</key>
+ <integer>8008</integer>
+
+ <!-- SSL port [0 = disable HTTPS] -->
+ <!-- (Must also configure SSLCertificate and SSLPrivateKey below) -->
+ <key>SSLPort</key>
+ <integer>8443</integer>
+
+ <!-- Redirect non-SSL ports to an SSL port -->
+ <key>RedirectHTTPToHTTPS</key>
+ <false/>
+
+ <!--
+ Network address configuration information
+
+ This configures the actual network address that the server binds to.
+ -->
+
+ <!-- List of IP addresses to bind to [empty = all] -->
+ <key>BindAddresses</key>
+ <array>
+ </array>
+
+ <!-- List of port numbers to bind to for HTTP [empty = same as "Port"] -->
+ <key>BindHTTPPorts</key>
+ <array>
+ </array>
+
+ <!-- List of port numbers to bind to for SSL [empty = same as "SSLPort"] -->
+ <key>BindSSLPorts</key>
+ <array>
+ </array>
+
+
+ <!--
+ Data Store
+ -->
+
+ <!-- Data root -->
+ <key>DataRoot</key>
+ <string>data/</string>
+
+ <!-- Document root -->
+ <key>DocumentRoot</key>
+ <string>twistedcaldav/test/data/</string>
+
+ <!-- Child aliases -->
+ <key>Aliases</key>
+ <dict>
+ <!--
+ <key>foo</key>
+ <dict>
+ <key>path</key>
+ <string>/path/to/foo</string>
+ </dict>
+ -->
+ </dict>
+
+ <!-- User quota (in bytes) -->
+ <key>UserQuota</key>
+ <integer>104857600</integer><!-- 100Mb -->
+
+ <!-- Attachment size limit (in bytes) -->
+ <key>MaximumAttachmentSize</key>
+ <integer>1048576</integer><!-- 1Mb -->
+
+ <!-- Maximum number of unique attendees per entire event -->
+ <!-- 0 for no limit -->
+ <key>MaxAttendeesPerInstance</key>
+ <integer>100</integer>
+
+ <!-- Maximum number of instances allowed for a single RRULE -->
+ <!-- 0 for no limit -->
+ <key>MaxInstancesForRRULE</key>
+ <integer>400</integer>
+
+
+ <!--
+ Directory service
+
+ A directory service provides information about principals (eg.
+ users, groups, locations and resources) to the server.
+
+ A variety of directory services are available for use.
+ -->
+
+ <!-- XML File Directory Service -->
+ <key>DirectoryService</key>
+ <dict>
+ <key>type</key>
+ <string>twistedcaldav.directory.xmlfile.XMLDirectoryService</string>
+
+ <key>params</key>
+ <dict>
+ <key>xmlFile</key>
+ <string>SET IN TEST</string>
+ <key>recordTypes</key>
+ <array>
+ <string>users</string>
+ <string>groups</string>
+ </array>
+ </dict>
+ </dict>
+
+ <!-- XML File Resource Service -->
+ <key>ResourceService</key>
+ <dict>
+ <key>Enabled</key>
+ <true/>
+ <key>type</key>
+ <string>twistedcaldav.directory.xmlfile.XMLDirectoryService</string>
+
+ <key>params</key>
+ <dict>
+ <key>xmlFile</key>
+ <string>SET IN TEST</string>
+ <key>recordTypes</key>
+ <array>
+ <string>resources</string>
+ <string>locations</string>
+ </array>
+ </dict>
+ </dict>
+
+ <!-- Open Directory Service (Mac OS X) -->
+ <!--
+ <key>DirectoryService</key>
+ <dict>
+ <key>type</key>
+ <string>twistedcaldav.directory.appleopendirectory.OpenDirectoryService</string>
+
+ <key>params</key>
+ <dict>
+ <key>node</key>
+ <string>/Search</string>
+ <key>cacheTimeout</key>
+ <integer>30</integer>
+ </dict>
+ </dict>
+ -->
+
+ <!--
+ Augment service
+
+ Augments for the directory service records to add calendar specific attributes.
+
+ A variety of augment services are available for use.
+ When using a partitioned server, a service that can be accessed from each host will be needed.
+ -->
+
+ <!-- XML File Augment Service -->
+ <key>AugmentService</key>
+ <dict>
+ <key>type</key>
+ <string>twistedcaldav.directory.augment.AugmentXMLDB</string>
+
+ <key>params</key>
+ <dict>
+ <key>xmlFiles</key>
+ <array>
+ <string>SET IN TEST</string>
+ </array>
+ </dict>
+ </dict>
+
+ <!-- Sqlite Augment Service -->
+ <!--
+ <key>AugmentService</key>
+ <dict>
+ <key>type</key>
+ <string>twistedcaldav.directory.augment.AugmentSqliteDB</string>
+
+ <key>params</key>
+ <dict>
+ <key>dbpath</key>
+ <string>/etc/caldavd/augments.sqlite</string>
+ </dict>
+ </dict>
+ -->
+
+ <!-- PostgreSQL Augment Service -->
+ <!--
+ <key>AugmentService</key>
+ <dict>
+ <key>type</key>
+ <string>twistedcaldav.directory.augment.AugmentPostgreSQLDB</string>
+
+ <key>params</key>
+ <dict>
+ <key>host</key>
+ <string>localhost</string>
+ <key>database</key>
+ <string>augments</string>
+ </dict>
+ </dict>
+ -->
+
+ <!-- Sqlite ProxyDB Service -->
+ <key>ProxyDBService</key>
+ <dict>
+ <key>type</key>
+ <string>twistedcaldav.directory.calendaruserproxy.ProxySqliteDB</string>
+
+ <key>params</key>
+ <dict>
+ <key>dbpath</key>
+ <string>data/proxies.sqlite</string>
+ </dict>
+ </dict>
+
+ <!-- PostgreSQL ProxyDB Service -->
+ <!--
+ <key>ProxyDBService</key>
+ <dict>
+ <key>type</key>
+ <string>twistedcaldav.directory.calendaruserproxy.ProxyPostgreSQLDB</string>
+
+ <key>params</key>
+ <dict>
+ <key>host</key>
+ <string>localhost</string>
+ <key>database</key>
+ <string>proxies</string>
+ </dict>
+ </dict>
+ -->
+
+ <key>ProxyLoadFromFile</key>
+ <string>conf/auth/proxies-test.xml</string>
+
+ <!--
+ Special principals
+
+ These principals are granted special access and/or perform
+ special roles on the server.
+ -->
+
+ <!-- Principals with "DAV:all" access (relative URLs) -->
+ <key>AdminPrincipals</key>
+ <array>
+ <string>/principals/__uids__/admin/</string>
+ </array>
+
+ <!-- Principals with "DAV:read" access (relative URLs) -->
+ <key>ReadPrincipals</key>
+ <array>
+ <!-- <string>/principals/__uids__/983C8238-FB6B-4D92-9242-89C0A39E5F81/</string> -->
+ </array>
+
+ <!-- Principals that can pose as other principals -->
+ <key>SudoersFile</key>
+ <string>conf/sudoers.plist</string>
+
+ <!-- Create "proxy access" principals -->
+ <key>EnableProxyPrincipals</key>
+ <true/>
+
+
+ <!--
+ Permissions
+ -->
+
+ <!-- Anonymous read access for root resource -->
+ <key>EnableAnonymousReadRoot</key>
+ <true/>
+
+ <!-- Anonymous read access for resource hierarchy -->
+ <key>EnableAnonymousReadNav</key>
+ <false/>
+
+ <!-- Enables directory listings for principals -->
+ <key>EnablePrincipalListings</key>
+ <true/>
+
+ <!-- Render calendar collections as a monolithic iCalendar object -->
+ <key>EnableMonolithicCalendars</key>
+ <true/>
+
+
+ <!--
+ Authentication
+ -->
+
+ <key>Authentication</key>
+ <dict>
+
+ <!-- Clear text; best avoided -->
+ <key>Basic</key>
+ <dict>
+ <key>Enabled</key>
+ <true/>
+ </dict>
+
+ <!-- Digest challenge/response -->
+ <key>Digest</key>
+ <dict>
+ <key>Enabled</key>
+ <true/>
+ <key>Algorithm</key>
+ <string>md5</string>
+ <key>Qop</key>
+ <string></string>
+ </dict>
+
+ <!-- Kerberos/SPNEGO -->
+ <key>Kerberos</key>
+ <dict>
+ <key>Enabled</key>
+ <false/>
+ <key>ServicePrincipal</key>
+ <string></string>
+ </dict>
+
+ <!-- Wikiserver authentication (Mac OS X) -->
+ <key>Wiki</key>
+ <dict>
+ <key>Enabled</key>
+ <true/>
+ <key>Cookie</key>
+ <string>sessionID</string>
+ <key>URL</key>
+ <string>http://127.0.0.1/RPC2</string>
+ <key>UserMethod</key>
+ <string>userForSession</string>
+ <key>WikiMethod</key>
+ <string>accessLevelForUserWikiCalendar</string>
+ </dict>
+
+ </dict>
+
+
+ <!--
+ Logging
+ -->
+
+ <!-- Apache-style access log -->
+ <key>AccessLogFile</key>
+ <string>logs/access.log</string>
+ <key>RotateAccessLog</key>
+ <false/>
+
+ <!-- Server activity log -->
+ <key>ErrorLogFile</key>
+ <string>logs/error.log</string>
+
+ <!-- Log levels -->
+ <key>DefaultLogLevel</key>
+ <string>info</string> <!-- debug, info, warn, error -->
+
+ <!-- Log level overrides for specific functionality -->
+ <key>LogLevels</key>
+ <dict>
+ <!--
+ <key>twistedcaldav.directory.appleopendirectory.OpenDirectoryService</key>
+ <string>debug</string>
+ -->
+ </dict>
+
+ <!-- Global server stats -->
+ <key>GlobalStatsSocket</key>
+ <string>logs/caldavd-stats.sock</string>
+
+ <!-- Global server stats logging period -->
+ <key>GlobalStatsLoggingPeriod</key>
+ <integer>60</integer>
+
+ <!-- Global server stats logging frequency [0 = disable stats] -->
+ <key>GlobalStatsLoggingFrequency</key>
+ <integer>12</integer>
+
+ <!-- Server statistics file -->
+ <key>ServerStatsFile</key>
+ <string>logs/stats.plist</string>
+
+ <!-- Server process ID file -->
+ <key>PIDFile</key>
+ <string>logs/caldavd.pid</string>
+
+
+ <!--
+ Accounting
+ -->
+
+ <!-- Enable accounting for certain operations -->
+ <key>AccountingCategories</key>
+ <dict>
+ <key>iTIP</key>
+ <false/>
+ <key>HTTP</key>
+ <false/>
+ </dict>
+ <!-- Enable accounting for specific principals -->
+ <key>AccountingPrincipals</key>
+ <array>
+ <!-- <string>/principals/__uids__/454D85C0-09F0-4DC6-A3C6-97DFEB4622CD/</string> -->
+ </array>
+
+
+ <!--
+ SSL/TLS
+ -->
+
+ <!-- Public key -->
+ <key>SSLCertificate</key>
+ <string>twistedcaldav/test/data/server.pem</string>
+
+ <!-- SSL authority chain (for intermediate certs) -->
+ <key>SSLAuthorityChain</key>
+ <string></string>
+
+ <!-- Private key -->
+ <key>SSLPrivateKey</key>
+ <string>twistedcaldav/test/data/server.pem</string>
+
+
+ <!--
+ Process management
+ -->
+
+ <key>UserName</key>
+ <string></string>
+
+ <key>GroupName</key>
+ <string></string>
+
+ <key>ProcessType</key>
+ <string>Combined</string>
+
+ <key>MultiProcess</key>
+ <dict>
+ <key>ProcessCount</key>
+ <integer>2</integer> <!-- 0 = larger of: 4 or (2 * CPU count) -->
+ </dict>
+
+
+ <!--
+ Notifications
+ -->
+
+ <key>Notifications</key>
+ <dict>
+ <!-- Time spent coalescing notifications before delivery -->
+ <key>CoalesceSeconds</key>
+ <integer>3</integer>
+
+ <key>InternalNotificationHost</key>
+ <string>localhost</string>
+
+ <key>InternalNotificationPort</key>
+ <integer>62309</integer>
+
+ <key>Services</key>
+ <dict>
+ <key>SimpleLineNotifier</key>
+ <dict>
+ <!-- Simple line notification service (for testing) -->
+ <key>Service</key>
+ <string>twistedcaldav.notify.SimpleLineNotifierService</string>
+ <key>Enabled</key>
+ <false/>
+ <key>Port</key>
+ <integer>62308</integer>
+ </dict>
+
+ <key>XMPPNotifier</key>
+ <dict>
+ <!-- XMPP notification service -->
+ <key>Service</key>
+ <string>twistedcaldav.notify.XMPPNotifierService</string>
+ <key>Enabled</key>
+ <false/>
+
+ <!-- XMPP host and port to contact -->
+ <key>Host</key>
+ <string>xmpp.host.name</string>
+ <key>Port</key>
+ <integer>5222</integer>
+
+ <!-- Jabber ID and password for the server -->
+ <key>JID</key>
+ <string>jid at xmpp.host.name/resource</string>
+ <key>Password</key>
+ <string>password_goes_here</string>
+
+ <!-- PubSub service address -->
+ <key>ServiceAddress</key>
+ <string>pubsub.xmpp.host.name</string>
+
+ <key>NodeConfiguration</key>
+ <dict>
+ <key>pubsub#deliver_payloads</key>
+ <string>1</string>
+ <key>pubsub#persist_items</key>
+ <string>1</string>
+ </dict>
+
+ <!-- Sends a presence notification to XMPP server at this interval (prevents disconnect) -->
+ <key>KeepAliveSeconds</key>
+ <integer>120</integer>
+
+ <!-- Sends a pubsub publish to a particular heartbeat node at this interval -->
+ <key>HeartbeatMinutes</key>
+ <integer>30</integer>
+
+ <!-- List of glob-like expressions defining which XMPP JIDs can converse with the server (for debugging) -->
+ <key>AllowedJIDs</key>
+ <array>
+ <!--
+ <string>*.example.com</string>
+ -->
+ </array>
+ </dict>
+ </dict>
+ </dict>
+
+
+ <!--
+ Server-to-server protocol
+ -->
+
+ <key>Scheduling</key>
+ <dict>
+
+ <!-- CalDAV protocol options -->
+ <key>CalDAV</key>
+ <dict>
+ <key>EmailDomain</key>
+ <string></string>
+ <key>HTTPDomain</key>
+ <string></string>
+ <key>AddressPatterns</key>
+ <array>
+ </array>
+ <key>OldDraftCompatibility</key>
+ <true/>
+ <key>ScheduleTagCompatibility</key>
+ <true/>
+ <key>EnablePrivateComments</key>
+ <true/>
+ </dict>
+
+ <!-- iSchedule protocol options -->
+ <key>iSchedule</key>
+ <dict>
+ <key>Enabled</key>
+ <false/>
+ <key>AddressPatterns</key>
+ <array>
+ </array>
+ <key>Servers</key>
+ <string>conf/servertoserver-test.xml</string>
+ </dict>
+
+ <!-- iMIP protocol options -->
+ <key>iMIP</key>
+ <dict>
+ <key>Enabled</key>
+ <false/>
+ <key>MailGatewayServer</key>
+ <string>localhost</string>
+ <key>MailGatewayPort</key>
+ <integer>62310</integer>
+ <key>Sending</key>
+ <dict>
+ <key>Server</key>
+ <string></string>
+ <key>Port</key>
+ <integer>587</integer>
+ <key>UseSSL</key>
+ <true/>
+ <key>Username</key>
+ <string></string>
+ <key>Password</key>
+ <string></string>
+ <key>Address</key>
+ <string></string> <!-- Address email will be sent from -->
+ </dict>
+ <key>Receiving</key>
+ <dict>
+ <key>Server</key>
+ <string></string>
+ <key>Port</key>
+ <integer>995</integer>
+ <key>Type</key>
+ <string></string> <!-- Either "pop" or "imap" -->
+ <key>UseSSL</key>
+ <true/>
+ <key>Username</key>
+ <string></string>
+ <key>Password</key>
+ <string></string>
+ <key>PollingSeconds</key>
+ <integer>30</integer>
+ </dict>
+ <key>AddressPatterns</key>
+ <array>
+ <string>mailto:.*</string>
+ </array>
+ </dict>
+
+ <!-- General options for scheduling -->
+ <key>Options</key>
+ <dict>
+ <key>AllowGroupAsOrganizer</key>
+ <false/>
+ <key>AllowLocationAsOrganizer</key>
+ <false/>
+ <key>AllowResourceAsOrganizer</key>
+ <false/>
+ </dict>
+
+ </dict>
+
+
+ <!--
+ Free-busy URL protocol
+ -->
+
+ <key>FreeBusyURL</key>
+ <dict>
+ <key>Enabled</key>
+ <true/>
+ <key>TimePeriod</key>
+ <integer>14</integer>
+ <key>AnonymousAccess</key>
+ <false/>
+ </dict>
+
+
+ <!--
+ Non-standard CalDAV extensions
+ -->
+
+ <!-- Calendar Drop Box -->
+ <key>EnableDropBox</key>
+ <true/>
+
+ <!-- Private Events -->
+ <key>EnablePrivateEvents</key>
+ <true/>
+
+ <!-- Timezone Service -->
+ <key>EnableTimezoneService</key>
+ <true/>
+
+
+ <!--
+ Miscellaneous items
+ -->
+
+ <!-- Service ACLs (Mac OS X) -->
+ <key>EnableSACLs</key>
+ <false/>
+
+ <!-- Web-based administration -->
+ <key>EnableWebAdmin</key>
+ <true/>
+
+ <!-- Support for Content-Encoding compression options as specified in RFC2616 Section 3.5 -->
+ <key>ResponseCompression</key>
+ <false/>
+
+ <!-- The retry-after value (in seconds) to return with a 503 error. -->
+ <key>HTTPRetryAfter</key>
+ <integer>180</integer>
+
+ <!-- A unix socket used for communication between the child and master processes.
+ An empty value tells the server to use a tcp socket instead. -->
+ <key>ControlSocket</key>
+ <string>logs/caldavd.sock</string>
+
+ <!-- Support for Memcached -->
+ <key>Memcached</key>
+ <dict>
+ <key>MaxClients</key>
+ <integer>5</integer>
+ <key>memcached</key>
+ <string>memcached</string> <!-- Find in PATH -->
+ <key>Options</key>
+ <array>
+ <!--<string>-vv</string>-->
+ </array>
+ </dict>
+
+ <!-- Response Caching -->
+ <key>ResponseCacheTimeout</key>
+ <integer>30</integer> <!-- in minutes -->
+
+
+ <!--
+ Twisted
+ -->
+
+ <key>Twisted</key>
+ <dict>
+ <key>twistd</key>
+ <string>../Twisted/bin/twistd</string>
+ </dict>
+
+
+ <key>Localization</key>
+ <dict>
+ <key>LocalesDirectory</key>
+ <string>locales</string>
+ <key>Language</key>
+ <string>English</string>
+ </dict>
+
+
+ </dict>
+</plist>
Added: CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/test/modify/resources-locations.xml
===================================================================
--- CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/test/modify/resources-locations.xml (rev 0)
+++ CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/test/modify/resources-locations.xml 2010-02-05 03:31:39 UTC (rev 5044)
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2006-2010 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.
+ -->
+
+<!DOCTYPE accounts SYSTEM "accounts.dtd">
+
+<accounts realm="Test Realm">
+ <location>
+ <uid>location01</uid>
+ <guid>location01</guid>
+ <password>test</password>
+ <name>Room 01</name>
+ </location>
+</accounts>
Added: CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/test/modify/users-groups.xml
===================================================================
--- CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/test/modify/users-groups.xml (rev 0)
+++ CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/test/modify/users-groups.xml 2010-02-05 03:31:39 UTC (rev 5044)
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2006-2010 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.
+ -->
+
+<!DOCTYPE accounts SYSTEM "accounts.dtd">
+
+<accounts realm="Test Realm">
+ <user repeat="10">
+ <uid>user%02d</uid>
+ <guid>user%02d</guid>
+ <password>test</password>
+ <name>Test User %02d</name>
+ <first-name>Test</first-name>
+ <last-name>User %02d</last-name>
+ </user>
+ <group>
+ <uid>testgroup1</uid>
+ <guid>e5a6142c-4189-4e9e-90b0-9cd0268b314b</guid>
+ <password>test</password>
+ <name>Group 01</name>
+ <members>
+ <member type="users">user01</member>
+ <member type="users">user02</member>
+ </members>
+ </group>
+</accounts>
Added: CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/test/test_modify.py
===================================================================
--- CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/test/test_modify.py (rev 0)
+++ CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/test/test_modify.py 2010-02-05 03:31:39 UTC (rev 5044)
@@ -0,0 +1,121 @@
+##
+# Copyright (c) 2005-2010 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.
+##
+
+import os
+from twistedcaldav.config import config
+from twistedcaldav.test.util import TestCase
+from calendarserver.tools.util import getDirectory
+from twisted.python.filepath import FilePath
+
+
+class ModificationTestCase(TestCase):
+
+ def xmlFile(self):
+ if not hasattr(self, "_xmlFile"):
+ self._xmlFile = FilePath(self.mktemp())
+ xmlFile.copyTo(self._xmlFile)
+ return self._xmlFile
+
+ def setUp(self):
+ testRoot = os.path.join(os.path.dirname(__file__), "modify")
+ configFileName = os.path.join(testRoot, "caldavd.plist")
+ config.load(configFileName)
+
+ usersFile = os.path.join(testRoot, "users-groups.xml")
+ config.DirectoryService.params.xmlFile = usersFile
+
+ # Copy xml file containgin locations/resources to a temp file because
+ # we're going to be modifying it during testing
+
+ origResourcesFile = FilePath(os.path.join(os.path.dirname(__file__),
+ "modify", "resources-locations.xml"))
+ copyResourcesFile = FilePath(self.mktemp())
+ origResourcesFile.copyTo(copyResourcesFile)
+ config.ResourceService.params.xmlFile = copyResourcesFile
+
+ augmentsFile = os.path.join(testRoot, "augments.xml")
+ config.AugmentService.params.xmlFiles = (augmentsFile,)
+
+ super(ModificationTestCase, self).setUp()
+
+ def test_createRecord(self):
+ directory = getDirectory()
+
+ record = directory.recordWithUID("resource01")
+ self.assertEquals(record, None)
+
+ directory.createRecord("resources", "resource01", shortNames=("resource01",), uid="resource01")
+
+ record = directory.recordWithUID("resource01")
+ self.assertNotEquals(record, None)
+
+ directory.createRecord("resources", "resource02", shortNames=("resource02",), uid="resource02")
+
+ record = directory.recordWithUID("resource02")
+ self.assertNotEquals(record, None)
+
+ # Make sure old records are still there:
+ record = directory.recordWithUID("resource01")
+ self.assertNotEquals(record, None)
+ record = directory.recordWithUID("location01")
+ self.assertNotEquals(record, None)
+
+ def test_destroyRecord(self):
+ directory = getDirectory()
+
+ record = directory.recordWithUID("resource01")
+ self.assertEquals(record, None)
+
+ directory.createRecord("resources", "resource01", shortNames=("resource01",), uid="resource01")
+
+ record = directory.recordWithUID("resource01")
+ self.assertNotEquals(record, None)
+
+ directory.destroyRecord("resources", "resource01")
+
+ record = directory.recordWithUID("resource01")
+ self.assertEquals(record, None)
+
+ # Make sure old records are still there:
+ record = directory.recordWithUID("location01")
+ self.assertNotEquals(record, None)
+
+ def test_updateRecord(self):
+ directory = getDirectory()
+
+ directory.createRecord("resources", "resource01",
+ shortNames=("resource01",), uid="resource01",
+ fullName="Resource number 1")
+
+ record = directory.recordWithUID("resource01")
+ self.assertEquals(record.fullName, "Resource number 1")
+
+ directory.updateRecord("resources", "resource01",
+ shortNames=("resource01", "r01"), uid="resource01",
+ fullName="Resource #1", firstName="First", lastName="Last",
+ emailAddresses=("resource01 at example.com", "r01 at example.com"))
+
+ record = directory.recordWithUID("resource01")
+ self.assertEquals(record.fullName, "Resource #1")
+ self.assertEquals(record.firstName, "First")
+ self.assertEquals(record.lastName, "Last")
+ self.assertEquals(set(record.shortNames), set(["resource01", "r01"]))
+ self.assertEquals(record.emailAddresses,
+ set(["resource01 at example.com", "r01 at example.com"]))
+
+ # Make sure old records are still there:
+ record = directory.recordWithUID("location01")
+ self.assertNotEquals(record, None)
Modified: CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/xmlfile.py
===================================================================
--- CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/xmlfile.py 2010-02-04 19:52:33 UTC (rev 5043)
+++ CalendarServer/branches/users/sagen/locations-resources/twistedcaldav/directory/xmlfile.py 2010-02-05 03:31:39 UTC (rev 5044)
@@ -33,8 +33,10 @@
from twistedcaldav.directory.directory import DirectoryService
from twistedcaldav.directory.cachingdirectory import CachingDirectoryService,\
CachingDirectoryRecord
-from twistedcaldav.directory.xmlaccountsparser import XMLAccountsParser
+from twistedcaldav.directory.xmlaccountsparser import XMLAccountsParser, XMLAccountRecord
+import xml.etree.ElementTree as ET
+
class XMLDirectoryService(CachingDirectoryService):
"""
XML based implementation of L{IDirectoryService}.
@@ -88,13 +90,6 @@
xmlPrincipal = xmlPrincipal,
)
- record = XMLDirectoryRecord(
- service = self,
- recordType = recordType,
- shortNames = tuple(xmlPrincipal.shortNames),
- xmlPrincipal = xmlPrincipal,
- )
-
# Look up augment information
# TODO: this needs to be deferred but for now we hard code the deferred result because
# we know it is completing immediately.
@@ -192,6 +187,131 @@
self._fileInfo = fileInfo
return self._parsedAccounts
+
+ def _addElement(self, parent, principal):
+ """
+ Create an XML element from principal and add it as a child of parent
+ """
+
+ # TODO: derive this from xmlaccountsparser.py
+ xmlTypes = {
+ 'users' : 'user',
+ 'groups' : 'group',
+ 'locations' : 'location',
+ 'resources' : 'resource',
+ }
+ xmlType = xmlTypes[principal.recordType]
+
+ element = ET.SubElement(parent, xmlType)
+ for value in principal.shortNames:
+ ET.SubElement(element, "uid").text = value
+ ET.SubElement(element, "guid").text = principal.guid
+ ET.SubElement(element, "name").text = principal.fullName
+ ET.SubElement(element, "first-name").text = principal.firstName
+ ET.SubElement(element, "last-name").text = principal.lastName
+ for value in principal.emailAddresses:
+ ET.SubElement(element, "email-address").text = value
+
+ return element
+
+ def _persistRecords(self, element):
+ # TODO: make this robust:
+ ET.ElementTree(element).write(self.xmlFile.path)
+
+ # Reload
+ self._initCaches(self.cacheClass) # nuke local cache
+ self._lastCheck = 0
+ self._accounts()
+ # TODO: nuke memcache entries, or prepopulate them
+
+
+ def createRecord(self, recordType, guid, shortNames=(), authIDs=set(),
+ fullName=None, firstName=None, lastName=None, emailAddresses=set(),
+ uid=None, password=None, **kwds):
+ """
+ Create and persist a record using the provided information. In this
+ XML-based implementation, the xml accounts are read in and converted
+ to elementtree elements, a new element is added for the new record,
+ and the document is serialized to disk.
+ """
+
+ # Make sure latest XML records are read in
+ self._lastCheck = 0
+ accounts = self._accounts()
+
+ accountsElement = ET.Element("accounts", realm=self.realmName)
+ for recType in self.recordTypes():
+ for xmlPrincipal in accounts[recType].itervalues():
+ self._addElement(accountsElement, xmlPrincipal)
+
+ xmlPrincipal = XMLAccountRecord(recordType)
+ xmlPrincipal.shortNames = shortNames
+ xmlPrincipal.guid = guid
+ xmlPrincipal.password = password
+ xmlPrincipal.fullName = fullName
+ xmlPrincipal.firstName = firstName
+ xmlPrincipal.lastName = lastName
+ xmlPrincipal.emailAddresses = emailAddresses
+ self._addElement(accountsElement, xmlPrincipal)
+
+ self._persistRecords(accountsElement)
+
+
+ def destroyRecord(self, recordType, guid):
+ """
+ Remove the record matching guid. In this XML-based implementation,
+ the xml accounts are read in and those not matching the given guid are
+ converted to elementtree elements, then the document is serialized to
+ disk.
+ """
+
+ # Make sure latest XML records are read in
+ self._lastCheck = 0
+ accounts = self._accounts()
+
+ accountsElement = ET.Element("accounts", realm=self.realmName)
+ for recType in self.recordTypes():
+
+ for xmlPrincipal in accounts[recType].itervalues():
+ if xmlPrincipal.guid != guid:
+ self._addElement(accountsElement, xmlPrincipal)
+
+ self._persistRecords(accountsElement)
+
+
+ def updateRecord(self, recordType, guid, shortNames=(), authIDs=set(),
+ fullName=None, firstName=None, lastName=None, emailAddresses=set(),
+ uid=None, password=None, **kwds):
+ """
+ Update the record matching guid. In this XML-based implementation,
+ the xml accounts are read in and converted to elementtree elements.
+ The account matching the given guid is replaced, then the document
+ is serialized to disk.
+ """
+
+ # Make sure latest XML records are read in
+ self._lastCheck = 0
+ accounts = self._accounts()
+
+ accountsElement = ET.Element("accounts", realm=self.realmName)
+ for recType in self.recordTypes():
+
+ for xmlPrincipal in accounts[recType].itervalues():
+ if xmlPrincipal.guid == guid:
+ # Replace this record
+ xmlPrincipal.shortNames = shortNames
+ xmlPrincipal.password = password
+ xmlPrincipal.fullName = fullName
+ xmlPrincipal.firstName = firstName
+ xmlPrincipal.lastName = lastName
+ xmlPrincipal.emailAddresses = emailAddresses
+ self._addElement(accountsElement, xmlPrincipal)
+ else:
+ self._addElement(accountsElement, xmlPrincipal)
+
+ self._persistRecords(accountsElement)
+
+
class XMLDirectoryRecord(CachingDirectoryRecord):
"""
XML based implementation implementation of L{IDirectoryRecord}.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20100204/3e540978/attachment-0001.html>
More information about the calendarserver-changes
mailing list