[CalendarServer-changes] [4529] CalendarServer/branches/users/cdaboo/deployment-partition-4524
source_changes at macosforge.org
source_changes at macosforge.org
Tue Sep 8 13:38:34 PDT 2009
Revision: 4529
http://trac.macosforge.org/projects/calendarserver/changeset/4529
Author: cdaboo at apple.com
Date: 2009-09-08 13:38:33 -0700 (Tue, 08 Sep 2009)
Log Message:
-----------
Partitioning support for augment DB and redirector back-ported to deployment branch.
Modified Paths:
--------------
CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/accounts-test.xml
CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/accounts.dtd
CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/accounts.xml
CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/caldavd-test.plist
CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/caldavd.plist
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/config.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/appleopendirectory.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/calendaruserproxy.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/directory.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/idirectory.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/principal.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/sudo.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/accounts.xml
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_aggregate.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_calendar.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_digest.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_guidchange.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_opendirectory.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_principal.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_proxyprincipalmembers.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_util.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_xmlfile.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/util.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/xmlaccountsparser.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/xmlfile.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/log.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/method/report_common.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/sql.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/static.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/tap.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_cache.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_config.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_log.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_memcache.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_memcachepool.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_memcacher.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_resource.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_root.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_static.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_tap.py
Added Paths:
-----------
CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/augments-test.xml
CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/augments.dtd
CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/partitions-test.plist
CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/partitions.plist
CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/proxies-test.xml
CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/proxies.dtd
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/database.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/augment.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/calendaruserproxyloader.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/augments-test.xml
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/augments.xml
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/proxies.xml
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_augment.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/xmlaugmentsparser.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/partitions.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_database.py
Removed Paths:
-------------
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/apache.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/sqldb.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/basic
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/digest
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/groups
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_apache.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_opendirectoryrecords.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_opendirectoryschema.py
CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_sqldb.py
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/accounts-test.xml
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/accounts-test.xml 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/accounts-test.xml 2009-09-08 20:38:33 UTC (rev 4529)
@@ -36,34 +36,24 @@
<guid>user%02d</guid>
<password>user%02d</password>
<name>User %02d</name>
- <cuaddr>mailto:user%02d at example.com</cuaddr>
</user>
<user repeat="10">
<uid>public%02d</uid>
<guid>public%02d</guid>
<password>public%02d</password>
<name>Public %02d</name>
- <cuaddr>mailto:public%02d at example.com</cuaddr>
</user>
<location repeat="10">
<uid>location%02d</uid>
<guid>location%02d</guid>
<password>location%02d</password>
<name>Room %02d</name>
- <auto-schedule/>
</location>
<resource repeat="10">
<uid>resource%02d</uid>
<guid>resource%02d</guid>
<password>resource%02d</password>
<name>Resource %02d</name>
- <auto-schedule/>
- <proxies>
- <member type="users">user01</member>
- </proxies>
- <read-only-proxies>
- <member type="users">user03</member>
- </read-only-proxies>
</resource>
<group>
<uid>group01</uid>
@@ -82,6 +72,5 @@
<members>
<member type="users">user01</member>
</members>
- <disable-calendar/>
</group>
</accounts>
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/accounts.dtd
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/accounts.dtd 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/accounts.dtd 2009-09-08 20:38:33 UTC (rev 4529)
@@ -1,5 +1,5 @@
<!--
-Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+Copyright (c) 2006-2009 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.
@@ -19,16 +19,16 @@
<!ELEMENT accounts (user*, group*, resource*, location*) >
<!ATTLIST accounts realm CDATA "">
- <!ELEMENT user (uid, guid, password, name, cuaddr*, disable-calendar?)>
+ <!ELEMENT user (uid*, guid, password?, name?, email-address*)>
<!ATTLIST user repeat CDATA "1">
- <!ELEMENT group (uid, guid, password, name, members, cuaddr*, disable-calendar?)>
+ <!ELEMENT group (uid*, guid, password?, name?, members)>
<!ATTLIST group repeat CDATA "1">
- <!ELEMENT resource (uid, guid, password, name, cuaddr*, auto-schedule?, proxies?, read-only-proxies?)>
+ <!ELEMENT resource (uid*, guid, password?, name)>
<!ATTLIST resource repeat CDATA "1">
- <!ELEMENT location (uid, guid, password, name, cuaddr*, auto-schedule?, proxies?, read-only-proxies?)>
+ <!ELEMENT location (uid*, guid, password?, name)>
<!ATTLIST location repeat CDATA "1">
<!ELEMENT member (#PCDATA)>
@@ -38,10 +38,6 @@
<!ELEMENT guid (#PCDATA)>
<!ELEMENT password (#PCDATA)>
<!ELEMENT name (#PCDATA)>
- <!ELEMENT cuaddr (#PCDATA)>
+ <!ELEMENT email-address (#PCDATA)>
<!ELEMENT members (member*)>
- <!ELEMENT auto-schedule EMPTY>
- <!ELEMENT disable-calendar EMPTY>
- <!ELEMENT proxies (member*)>
- <!ELEMENT read-only-proxies (member*)>
>
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/accounts.xml
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/accounts.xml 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/accounts.xml 2009-09-08 20:38:33 UTC (rev 4529)
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+Copyright (c) 2006-2009 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.
@@ -28,7 +28,6 @@
<uid>test</uid>
<password>test</password>
<name>Test User</name>
- <cuaddr>mailto:testuser at example.com</cuaddr>
</user>
<group>
<uid>users</uid>
@@ -42,10 +41,6 @@
<uid>mercury</uid>
<password>mercury</password>
<name>Mecury Conference Room, Building 1, 2nd Floor</name>
- <auto-schedule/>
- <proxies>
- <member type="users">test</member>
- </proxies>
</location>
</accounts>
Added: CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/augments-test.xml
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/augments-test.xml (rev 0)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/augments-test.xml 2009-09-08 20:38:33 UTC (rev 4529)
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2006-2009 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 augments SYSTEM "augments.dtd">
+
+<augments>
+ <record>
+ <guid>admin</guid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ </record>
+ <record>
+ <guid>apprentice</guid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ </record>
+ <record repeat="99">
+ <guid>user%02d</guid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ <cuaddr>mailto:user%02d at example.com</cuaddr>
+ </record>
+ <record repeat="10">
+ <guid>public%02d</guid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ <cuaddr>mailto:public%02d at example.com</cuaddr>
+ </record>
+ <record repeat="10">
+ <guid>location%02d</guid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ <auto-schedule>true</auto-schedule>
+ </record>
+ <record repeat="10">
+ <guid>resource%02d</guid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ <auto-schedule>true</auto-schedule>
+ </record>
+ <record repeat="4">
+ <guid>group%02d</guid>
+ <enable>true</enable>
+ </record>
+ <record>
+ <guid>disabledgroup</guid>
+ <enable>false</enable>
+ </record>
+</augments>
Added: CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/augments.dtd
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/augments.dtd (rev 0)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/augments.dtd 2009-09-08 20:38:33 UTC (rev 4529)
@@ -0,0 +1,28 @@
+<!--
+Copyright (c) 2006-2009 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.
+-->
+
+<!ELEMENT augments (record*) >
+
+ <!ELEMENT record (guid, enable, hosted-at?, enable-calendar?, auto-schedule?, cuaddr*)>
+ <!ATTLIST record repeat CDATA "1">
+
+ <!ELEMENT guid (#PCDATA)>
+ <!ELEMENT enable (#PCDATA)>
+ <!ELEMENT hosted-at (#PCDATA)>
+ <!ELEMENT enable-calendar (#PCDATA)>
+ <!ELEMENT auto-schedule (#PCDATA)>
+ <!ELEMENT cuaddr (#PCDATA)>
+>
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/caldavd-test.plist
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/caldavd-test.plist 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/caldavd-test.plist 2009-09-08 20:38:33 UTC (rev 4529)
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
-Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+Copyright (c) 2006-2009 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.
@@ -116,63 +116,53 @@
<dict>
<key>node</key>
<string>/Search</string>
- <key>requireComputerRecord</key>
- <true/>
+ <key>cacheTimeout</key>
+ <integer>30</integer>
</dict>
</dict>
-->
- <!-- Apache-style Basic Directory Service -->
- <!--
- <key>DirectoryService</key>
- <dict>
- <key>type</key>
- <string>twistedcaldav.directory.apache.BasicDirectoryService</string>
-
- <key>params</key>
- <dict>
- <key>userFile</key>
- <string>conf/basic</string>
- <key>groupFile</key>
- <string>conf/group</string>
- </dict>
- </dict>
- -->
+ <!--
+ Augment service
- <!-- Apache-style Digest Directory Service -->
- <!--
- <key>DirectoryService</key>
- <dict>
- <key>type</key>
- <string>twistedcaldav.directory.apache.DigestDirectoryService</string>
-
- <key>params</key>
+ 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>userFile</key>
- <string>conf/digest</string>
- <key>groupFile</key>
- <string>conf/group</string>
+ <key>type</key>
+ <string>twistedcaldav.directory.augment.AugmentXMLDB</string>
+
+ <key>params</key>
+ <dict>
+ <key>xmlFiles</key>
+ <array>
+ <string>conf/auth/augments-test.xml</string>
+ </array>
+ </dict>
</dict>
- </dict>
- -->
-
- <!-- SQL Directory Service -->
- <!--
- <key>DirectoryService</key>
- <dict>
- <key>type</key>
- <string>twistedcaldav.directory.sqldb.SQLDirectoryService</string>
-
- <key>params</key>
+
+ <!-- Sqlite Augment Service -->
+ <!--
+ <key>AugmentService</key>
<dict>
- <key>dbParentPath</key>
- <string>twistedcaldav/test/data/</string>
- <key>xmlFile</key>
- <string>conf/accounts-test.xml</string>
+ <key>type</key>
+ <string>twistedcaldav.directory.augment.AugmentSqliteDB</string>
+
+ <key>params</key>
+ <dict>
+ <key>dbpath</key>
+ <string>/etc/caldavd/augments.sqlite</string>
+ </dict>
</dict>
- </dict>
- -->
+ -->
+ <key>ProxyLoadFromFile</key>
+ <string>conf/auth/proxies-test.xml</string>
<!--
Special principals
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/caldavd.plist
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/caldavd.plist 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/caldavd.plist 2009-09-08 20:38:33 UTC (rev 4529)
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
- Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+ Copyright (c) 2006-2009 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.
@@ -120,13 +120,51 @@
<dict>
<key>node</key>
<string>/Search</string>
- <key>requireComputerRecord</key>
- <true/>
+ <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>/etc/caldavd/augments.xml</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>
+ -->
+
+ <!--
Special principals
These principals are granted special access and/or perform
Added: CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/partitions-test.plist
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/partitions-test.plist (rev 0)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/partitions-test.plist 2009-09-08 20:38:33 UTC (rev 4529)
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Copyright (c) 2009 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>
+ <key>partitions</key>
+ <array>
+ <dict>
+ <key>uid</key>
+ <string>00001</string>
+ <key>url</key>
+ <string>http://localhost:8008</string>
+ </dict>
+ <dict>
+ <key>uid</key>
+ <string>00002</string>
+ <key>url</key>
+ <string>http://localhost:8108</string>
+ </dict>
+ </array>
+</dict>
+</plist>
Added: CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/partitions.plist
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/partitions.plist (rev 0)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/partitions.plist 2009-09-08 20:38:33 UTC (rev 4529)
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ Copyright (c) 2009 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>
+ <key>partitions</key>
+ <array>
+ <!--
+ <dict>
+ <key>uid</key>
+ <string>00001</string>
+ <key>url</key>
+ <string>http://localhost:8008</string>
+ </dict>
+ -->
+ </array>
+</dict>
+</plist>
Added: CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/proxies-test.xml
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/proxies-test.xml (rev 0)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/proxies-test.xml 2009-09-08 20:38:33 UTC (rev 4529)
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2006-2009 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 proxies SYSTEM "proxies.dtd">
+
+<proxies>
+ <record repeat="10">
+ <guid>resource%02d</guid>
+ <proxies>
+ <member>user01</member>
+ </proxies>
+ <read-only-proxies>
+ <member>user03</member>
+ </read-only-proxies>
+ </record>
+</proxies>
Added: CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/proxies.dtd
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/proxies.dtd (rev 0)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/conf/proxies.dtd 2009-09-08 20:38:33 UTC (rev 4529)
@@ -0,0 +1,26 @@
+<!--
+Copyright (c) 2006-2009 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.
+-->
+
+<!ELEMENT proxies (record*) >
+
+ <!ELEMENT record (guid, proxies?, read-only-proxies?)>
+ <!ATTLIST record repeat CDATA "1">
+
+ <!ELEMENT guid (#PCDATA)>
+ <!ELEMENT proxies (member*)>
+ <!ELEMENT read-only-proxies (member*)>
+ <!ELEMENT member (#PCDATA)>
+>
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/config.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/config.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/config.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -23,7 +23,6 @@
"config",
]
-import os
import copy
import re
@@ -33,6 +32,7 @@
from twistedcaldav.py.plistlib import readPlist
from twistedcaldav.log import Logger
from twistedcaldav.log import clearLogLevels, setLogLevelForNamespace, InvalidLogLevelError
+from twistedcaldav.partitions import partitions
log = Logger()
@@ -71,11 +71,20 @@
},
"twistedcaldav.directory.appleopendirectory.OpenDirectoryService": {
"node": "/Search",
- "requireComputerRecord": True,
"cacheTimeout": 30,
},
}
+augmentDefaultParams = {
+ "twistedcaldav.directory.augment.AugmentXMLDB": {
+ "xmlFiles": ["/etc/caldavd/augments.xml",],
+ },
+ "twistedcaldav.directory.augment.AugmentSqliteDB": {
+ "dbpath": "/etc/caldavd/augments.sqlite",
+ },
+}
+
+
defaultConfig = {
# Note: Don't use None values below; that confuses the command-line parser.
@@ -121,6 +130,23 @@
},
#
+ # Augment service
+ #
+ # Augments for the directory service records to add calendar specific attributes.
+ #
+ "AugmentService": {
+ "type": "twistedcaldav.directory.augment.AugmentXMLDB",
+ "params": augmentDefaultParams["twistedcaldav.directory.augment.AugmentXMLDB"],
+ },
+
+ #
+ # Proxy loader
+ #
+ # Allows for initialization of the proxy database from an XML file.
+ #
+ "ProxyLoadFromFile": "",
+
+ #
# Special principals
#
"AdminPrincipals": [], # Principals with "DAV:all" access (relative URLs)
@@ -254,6 +280,13 @@
},
#
+ # Partitioning
+ #
+ "EnablePartitions": False, # Partitioning enabled or not
+ "ServerPartitionID": "", # Unique ID for this server's partition instance.
+ "PartitionConfigFile": "/etc/caldavd/partitions.plist", # File path for partition information
+
+ #
# Performance tuning
#
@@ -351,6 +384,7 @@
self.updateDropBox,
self.updateLogLevels,
self.updateNotifications,
+ self.updatePartitions,
]
def __str__(self):
@@ -408,6 +442,12 @@
if param not in serviceDefaultParams[self._data.DirectoryService.type]:
del self._data.DirectoryService.params[param]
+ if self._data.AugmentService.type in augmentDefaultParams:
+ for param in tuple(self._data.AugmentService.params):
+ if param not in augmentDefaultParams[self._data.AugmentService.type]:
+ log.warn("Parameter %s is not supported by service %s" % (param, self._data.AugmentService.type))
+ del self._data.AugmentService.params[param]
+
@staticmethod
def updateACLs(self, items):
#
@@ -518,6 +558,19 @@
except InvalidLogLevelError, e:
raise ConfigurationError("Invalid log level: %s" % (e.level))
+ @staticmethod
+ def updatePartitions(self, items):
+ #
+ # Partitions
+ #
+
+ if "EnablePartitions" in items:
+ if items["EnablePartitions"]:
+ partitions.setSelfPartition(items.get("ServerPartitionID", ""))
+ partitions.readConfig(items.get("PartitionConfigFile", ""))
+ else:
+ partitions.clear()
+
def updateDefaults(self, items):
_mergeData(self._defaults, items)
self.update(items)
Added: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/database.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/database.py (rev 0)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/database.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -0,0 +1,366 @@
+##
+# Copyright (c) 2009 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.
+##
+
+from twistedcaldav.log import Logger
+
+from twisted.enterprise.adbapi import ConnectionPool
+from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.python.threadpool import ThreadPool
+
+import thread
+
+"""
+Generic ADAPI database access object.
+"""
+
+__all__ = [
+ "AbstractADBAPIDatabase",
+]
+
+log = Logger()
+
+class ConnectionClosingThreadPool(ThreadPool):
+ """
+ A ThreadPool that closes connections for each worker thread
+ """
+
+ def _worker(self, o):
+ log.debug("Starting ADBAPI thread: %s" % (thread.get_ident(),))
+ ThreadPool._worker(self, o)
+ self._closeConnection()
+
+ def _closeConnection(self):
+
+ tid = thread.get_ident()
+ log.debug("Closing ADBAPI thread: %s" % (tid,))
+
+ conn = self.pool.connections.get(tid)
+ self.pool._close(conn)
+ del self.pool.connections[tid]
+
+class AbstractADBAPIDatabase(object):
+ """
+ A generic SQL database.
+ """
+
+ def __init__(self, dbID, dbapiName, dbapiArgs, persistent, **kwargs):
+ """
+
+ @param persistent: C{True} if the data in the DB must be perserved during upgrades,
+ C{False} if the DB data can be re-created from an external source.
+ @type persistent: bool
+ """
+ self.dbID = dbID
+ self.dbapiName = dbapiName
+ self.dbapiArgs = dbapiArgs
+ self.dbapikwargs = kwargs
+
+ self.persistent = persistent
+
+ self.initialized = False
+
+ def __repr__(self):
+ return "<%s %r>" % (self.__class__.__name__, self.pool)
+
+ @inlineCallbacks
+ def open(self):
+ """
+ Access the underlying database.
+ @return: a db2 connection object for this index's underlying data store.
+ """
+ if not self.initialized:
+
+ self.pool = ConnectionPool(self.dbapiName, *self.dbapiArgs, **self.dbapikwargs)
+
+ # sqlite3 is not thread safe which means we have to close the sqlite3 connections in the same thread that
+ # opened them. We need a special thread pool class that has a thread worker function that does a close
+ # when a thread is closed.
+ if self.dbapiName == "sqlite3":
+ self.pool.threadpool.stop()
+ self.pool.threadpool = ConnectionClosingThreadPool(1, 1)
+ self.pool.threadpool.start()
+ self.pool.threadpool.pool = self.pool
+
+ #
+ # Set up the schema
+ #
+ # Create CALDAV table if needed
+
+ test = (yield self._test_schema_table())
+ if test:
+ version = (yield self._db_value_for_sql("select VALUE from CALDAV where KEY = 'SCHEMA_VERSION'"))
+ dbtype = (yield self._db_value_for_sql("select VALUE from CALDAV where KEY = 'TYPE'"))
+
+ if (version != self._db_version()) or (dbtype != self._db_type()):
+
+ if dbtype != self._db_type():
+ log.err("Database %s has different type (%s vs. %s)"
+ % (self.dbID, dbtype, self._db_type()))
+
+ # Delete this index and start over
+ yield self._db_remove()
+ yield self._db_init()
+
+ elif version != self._db_version():
+ log.err("Database %s has different schema (v.%s vs. v.%s)"
+ % (self.dbID, version, self._db_version()))
+
+ # Upgrade the DB
+ yield self._db_upgrade(version)
+
+ else:
+ yield self._db_init()
+ self.initialized = True
+
+ def close(self):
+
+ if self.initialized:
+ self.pool.close()
+ self.pool = None
+ self.initialized = False
+
+ @inlineCallbacks
+ def execute(self, sql, *query_params):
+
+ if not self.initialized:
+ yield self.open()
+
+ yield self._db_execute(sql, *query_params)
+
+ @inlineCallbacks
+ def executescript(self, script):
+
+ if not self.initialized:
+ yield self.open()
+
+ yield self._db_execute_script(script)
+
+ @inlineCallbacks
+ def query(self, sql, *query_params):
+
+ if not self.initialized:
+ yield self.open()
+
+ result = (yield self._db_all_values_for_sql(sql, *query_params))
+ returnValue(result)
+
+ @inlineCallbacks
+ def queryList(self, sql, *query_params):
+
+ if not self.initialized:
+ yield self.open()
+
+ result = (yield self._db_values_for_sql(sql, *query_params))
+ returnValue(result)
+
+ @inlineCallbacks
+ def queryOne(self, sql, *query_params):
+
+ if not self.initialized:
+ yield self.open()
+
+ result = (yield self._db_value_for_sql(sql, *query_params))
+ returnValue(result)
+
+ def _db_version(self):
+ """
+ @return: the schema version assigned to this DB.
+ """
+ raise NotImplementedError
+
+ def _db_type(self):
+ """
+ @return: the collection type assigned to this DB.
+ """
+ raise NotImplementedError
+
+ @inlineCallbacks
+ def _test_schema_table(self):
+ result = (yield self._db_value_for_sql("""
+ select (1) from SQLITE_MASTER
+ where TYPE = 'table' and NAME = 'CALDAV'
+ """))
+ returnValue(result)
+
+ @inlineCallbacks
+ def _db_init(self):
+ """
+ Initialise the underlying database tables.
+ """
+ log.msg("Initializing database %s" % (self.dbID,))
+
+ # TODO we need an exclusive lock of some kind here to prevent a race condition
+ # in which multiple processes try to create the tables.
+
+
+ yield self._db_init_schema_table()
+ yield self._db_init_data_tables()
+ yield self._db_recreate()
+
+ @inlineCallbacks
+ def _db_init_schema_table(self):
+ """
+ Initialise the underlying database tables.
+ @param db_filename: the file name of the index database.
+ @param q: a database cursor to use.
+ """
+
+ #
+ # CALDAV table keeps track of our schema version and type
+ #
+ yield self._db_execute(
+ """
+ create table if not exists CALDAV (
+ KEY text unique,
+ VALUE text unique
+ )
+ """
+ )
+ yield self._db_execute(
+ """
+ insert or ignore into CALDAV (KEY, VALUE)
+ values ('SCHEMA_VERSION', :1)
+ """, (self._db_version(),)
+ )
+ yield self._db_execute(
+ """
+ insert or ignore into CALDAV (KEY, VALUE)
+ values ('TYPE', :1)
+ """, (self._db_type(),)
+ )
+
+ def _db_init_data_tables(self):
+ """
+ Initialise the underlying database tables.
+ """
+ raise NotImplementedError
+
+ def _db_recreate(self):
+ """
+ Recreate the database tables.
+ """
+
+ # Implementations can override this to re-create data
+ pass
+
+ @inlineCallbacks
+ def _db_upgrade(self, old_version):
+ """
+ Upgrade the database tables.
+ """
+
+ if self.persistent:
+ yield self._db_upgrade_data_tables(old_version)
+ yield self._db_upgrade_schema()
+ else:
+ # Non-persistent DB's by default can be removed and re-created. However, for simple
+ # DB upgrades they SHOULD override this method and handle those for better performance.
+ yield self._db_remove()
+ yield self._db_init()
+
+ def _db_upgrade_data_tables(self, old_version):
+ """
+ Upgrade the data from an older version of the DB.
+ """
+ # Persistent DB's MUST override this method and do a proper upgrade. Their data
+ # cannot be thrown away.
+ raise NotImplementedError("Persistent databases MUST support an upgrade method.")
+
+ @inlineCallbacks
+ def _db_upgrade_schema(self):
+ """
+ Upgrade the stored schema version to the current one.
+ """
+ yield self._db_execute("insert or replace into CALDAV (KEY, VALUE) values ('SCHEMA_VERSION', :1)", (self._db_version(),))
+
+ @inlineCallbacks
+ def _db_remove(self):
+ """
+ Remove all database information (all the tables)
+ """
+ yield self._db_remove_data_tables()
+ yield self._db_remove_schema()
+
+ def _db_remove_data_tables(self):
+ """
+ Remove all the data from an older version of the DB.
+ """
+ raise NotImplementedError("Each database must remove its own tables.")
+
+ @inlineCallbacks
+ def _db_remove_schema(self):
+ """
+ Remove the stored schema version table.
+ """
+ yield self._db_execute("drop table if exists CALDAV")
+
+ @inlineCallbacks
+ def _db_all_values_for_sql(self, sql, *query_params):
+ """
+ Execute an SQL query and obtain the resulting values.
+ @param sql: the SQL query to execute.
+ @param query_params: parameters to C{sql}.
+ @return: an interable of values in the first column of each row
+ resulting from executing C{sql} with C{query_params}.
+ @raise AssertionError: if the query yields multiple columns.
+ """
+
+ results = (yield self.pool.runQuery(sql, *query_params))
+ returnValue(tuple(results))
+
+ @inlineCallbacks
+ def _db_values_for_sql(self, sql, *query_params):
+ """
+ Execute an SQL query and obtain the resulting values.
+
+ @param sql: the SQL query to execute.
+ @param query_params: parameters to C{sql}.
+ @return: an interable of values in the first column of each row
+ resulting from executing C{sql} with C{query_params}.
+ @raise AssertionError: if the query yields multiple columns.
+ """
+
+ results = (yield self.pool.runQuery(sql, *query_params))
+ returnValue(tuple([row[0] for row in results]))
+
+ @inlineCallbacks
+ def _db_value_for_sql(self, sql, *query_params):
+ """
+ Execute an SQL query and obtain a single value.
+
+ @param sql: the SQL query to execute.
+ @param query_params: parameters to C{sql}.
+ @return: the value resulting from the executing C{sql} with
+ C{query_params}.
+ @raise AssertionError: if the query yields multiple rows or columns.
+ """
+ value = None
+ for row in (yield self._db_values_for_sql(sql, *query_params)):
+ assert value is None, "Multiple values in DB for %s %s" % (sql, query_params)
+ value = row
+ returnValue(value)
+
+ def _db_execute(self, sql, *query_params):
+ """
+ Execute an SQL operation that returns None.
+
+ @param sql: the SQL query to execute.
+ @param query_params: parameters to C{sql}.
+ @return: an iterable of tuples for each row resulting from executing
+ C{sql} with C{query_params}.
+ """
+
+ return self.pool.runOperation(sql, *query_params)
Deleted: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/apache.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/apache.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/apache.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -1,197 +0,0 @@
-##
-# Copyright (c) 2006-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.
-##
-
-"""
-Apache UserFile/GroupFile compatible directory service implementation.
-"""
-
-__all__ = [
- "BasicDirectoryService",
- "DigestDirectoryService",
-]
-
-from crypt import crypt
-
-from twisted.python.filepath import FilePath
-from twisted.cred.credentials import UsernamePassword
-
-from twistedcaldav.directory.directory import DirectoryService, DirectoryRecord
-from twistedcaldav.directory.directory import UnknownRecordTypeError
-
-class AbstractDirectoryService(DirectoryService):
- """
- Abstract Apache-compatible implementation of L{IDirectoryService}.
- """
- def __repr__(self):
- return "<%s %r: %r %r>" % (self.__class__.__name__, self.realmName, self.userFile, self.groupFile)
-
- def __init__(self, realmName, userFile, groupFile=None):
- super(AbstractDirectoryService, self).__init__()
-
- if type(userFile) is str:
- userFile = FilePath(userFile)
- if type(groupFile) is str:
- groupFile = FilePath(groupFile)
-
- self.realmName = realmName
- self.userFile = userFile
- self.groupFile = groupFile
-
- def recordTypes(self):
- recordTypes = (DirectoryService.recordType_users,)
- if self.groupFile is not None:
- recordTypes += (DirectoryService.recordType_groups,)
- return recordTypes
-
- def listRecords(self, recordType):
- for entryShortName, entryData in self.entriesForRecordType(recordType):
- if recordType == DirectoryService.recordType_users:
- yield self.userRecordClass(
- service = self,
- recordType = recordType,
- shortName = entryShortName,
- cryptPassword = entryData,
- )
-
- elif recordType == DirectoryService.recordType_groups:
- yield GroupRecord(
- service = self,
- recordType = recordType,
- shortName = entryShortName,
- members = entryData,
- )
-
- else:
- # Subclass should cover the remaining record types
- raise AssertionError("Unknown record type: %r" % (recordType,))
-
- def recordWithShortName(self, recordType, shortName):
- for entryShortName, entryData in self.entriesForRecordType(recordType):
- if entryShortName == shortName:
- if recordType == DirectoryService.recordType_users:
- return self.userRecordClass(
- service = self,
- recordType = recordType,
- shortName = entryShortName,
- cryptPassword = entryData,
- )
-
- if recordType == DirectoryService.recordType_groups:
- return GroupRecord(
- service = self,
- recordType = recordType,
- shortName = entryShortName,
- members = entryData,
- )
-
- # Subclass should cover the remaining record types
- raise AssertionError("Unknown record type: %r" % (recordType,))
-
- return None
-
- def entriesForRecordType(self, recordType):
- if recordType == DirectoryService.recordType_users:
- recordFile = self.userFile
- elif recordType == DirectoryService.recordType_groups:
- recordFile = self.groupFile
- else:
- raise UnknownRecordTypeError("Unknown record type: %s" % (recordType,))
-
- if recordFile is None:
- return
-
- for entry in recordFile.open():
- if entry and entry[0] != "#":
- shortName, rest = entry.rstrip("\n").split(":", 1)
- yield shortName, rest
-
-class AbstractDirectoryRecord(DirectoryRecord):
- """
- Abstract Apache-compatible implementation of L{IDirectoryRecord}.
- """
- def __init__(self, service, recordType, shortName):
- super(AbstractDirectoryRecord, self).__init__(
- service = service,
- recordType = recordType,
- guid = None,
- shortName = shortName,
- fullName = None,
- calendarUserAddresses = set(),
- autoSchedule = False,
- )
-
-class AbstractUserRecord(AbstractDirectoryRecord):
- def __init__(self, service, recordType, shortName, cryptPassword=None):
- super(AbstractUserRecord, self).__init__(service, recordType, shortName)
-
- self._cryptPassword = cryptPassword
-
- def groups(self):
- for group in self.service.listRecords(DirectoryService.recordType_groups):
- for member in group.members():
- if member == self:
- yield group
- continue
-
-class BasicUserRecord(AbstractUserRecord):
- """
- Apache UserFile implementation of L{IDirectoryRecord}.
- """
- def verifyCredentials(self, credentials):
- if self._cryptPassword in ("", "*", "x"):
- return False
-
- if isinstance(credentials, UsernamePassword):
- return crypt(credentials.password, self._cryptPassword) == self._cryptPassword
-
- return super(BasicUserRecord, self).verifyCredentials(credentials)
-
-class BasicDirectoryService(AbstractDirectoryService):
- """
- Apache UserFile/GroupFile implementation of L{IDirectoryService}.
- """
- baseGUID = "DDF1E45C-CADE-4FCD-8AE6-B4B41D72B325"
- userRecordClass = BasicUserRecord
-
-class DigestUserRecord(AbstractUserRecord):
- """
- Apache DigestUserFile implementation of L{IDirectoryRecord}.
- """
- def verifyCredentials(self, credentials):
- raise NotImplementedError()
-
-class DigestDirectoryService(AbstractDirectoryService):
- """
- Apache DigestUserFile/GroupFile implementation of L{IDirectoryService}.
- """
- baseGUID = "0C719D1B-0A14-4074-8740-6D96A7D0C787"
- userRecordClass = DigestUserRecord
-
-class GroupRecord(AbstractDirectoryRecord):
- """
- Apache GroupFile implementation of L{IDirectoryRecord}.
- """
- def __init__(self, service, recordType, shortName, members=()):
- super(GroupRecord, self).__init__(service, recordType, shortName)
-
- if type(members) is str:
- members = tuple(m.strip() for m in members.split(","))
-
- self._members = members
-
- def members(self):
- for shortName in self._members:
- yield self.service.recordWithShortName(DirectoryService.recordType_users, shortName)
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/appleopendirectory.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/appleopendirectory.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/appleopendirectory.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -26,10 +26,7 @@
import sys
import os
from random import random
-from uuid import UUID
-from xml.parsers.expat import ExpatError
-
import opendirectory
import dsattributes
import dsquery
@@ -39,12 +36,10 @@
from twisted.cred.credentials import UsernamePassword
from twisted.web2.auth.digest import DigestedCredentials
-from twistedcaldav.config import config
+from twistedcaldav.directory import augment
from twistedcaldav.directory.directory import DirectoryService, DirectoryRecord
from twistedcaldav.directory.directory import DirectoryError, UnknownRecordTypeError
-from plistlib import readPlistFromString, readPlist
-
serverPreferences = '/Library/Preferences/com.apple.servermgr_info.plist'
saclGroup = 'com.apple.access_calendar'
@@ -57,11 +52,9 @@
def __repr__(self):
return "<%s %r: %r>" % (self.__class__.__name__, self.realmName, self.node)
- def __init__(self, node="/Search", requireComputerRecord=True, dosetup=True, cacheTimeout=30):
+ def __init__(self, node="/Search", dosetup=True, cacheTimeout=30):
"""
@param node: an OpenDirectory node name to bind to.
- @param requireComputerRecord: C{True} if the directory schema is to be used to determine
- which calendar users are enabled.
@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.
@@ -75,9 +68,6 @@
self.realmName = node
self.directory = directory
self.node = node
- self.requireComputerRecord = requireComputerRecord
- self.computerRecords = {}
- self.servicetags = set()
self.cacheTimeout = cacheTimeout
self._records = {}
self._delayedCalls = set()
@@ -85,21 +75,6 @@
self.isWorkgroupServer = False
if dosetup:
- if self.requireComputerRecord:
- try:
- self._lookupVHostRecord()
- except Exception, e:
- self.log_error("Unable to locate virtual host record: %s" % (e,))
- raise
-
- if os.path.exists(serverPreferences):
- serverInfo = readPlist(serverPreferences)
-
- self.isWorkgroupServer = serverInfo.get('ServiceConfig', {}).get('IsWorkgroupServer', False)
-
- if self.isWorkgroupServer:
- self.log_info("Enabling Workgroup Server compatibility mode")
-
for recordType in self.recordTypes():
self.recordsForType(recordType)
@@ -174,216 +149,6 @@
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 apple-serviceinfo attribute value equal to the virtual hostname
- # and return some useful attributes.
- attrs = [
- dsattributes.kDS1AttrGeneratedUID,
- dsattributes.kDSNAttrRecordName,
- dsattributes.kDSNAttrMetaNodeLocation,
- "dsAttrTypeNative:apple-serviceinfo",
- ]
-
- self.log_debug("opendirectory.queryRecordsWithAttribute_list(%r,%r,%r,%r,%r)" % (
- self.directory,
- dsquery.match(
- "dsAttrTypeNative:apple-serviceinfo",
- vhostname,
- dsattributes.eDSContains,
- ).generate(),
- True, # case insentive for hostnames
- dsattributes.kDSStdRecordTypeComputers,
- attrs
- ))
- records = opendirectory.queryRecordsWithAttributes_list(
- self.directory,
- dsquery.match(
- "dsAttrTypeNative:apple-serviceinfo",
- vhostname,
- dsattributes.eDSContains,
- ).generate(),
- True, # case insentive for hostnames
- 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 all appropriate records and determine the enabled (only) service tags for each.
- for recordname, record in records:
- self._parseServiceInfo(vhostname, recordname, record)
-
- # Log all the matching records
- for key, value in self.computerRecords.iteritems():
- _ignore_recordname, enabled, servicetag = value
- self.log_info("Matched Directory record: %s with ServicesLocator: %s, state: %s" % (
- key,
- servicetag,
- {True:"enabled", False:"disabled"}[enabled]
- ))
-
- # Log all the enabled service tags - or generate an error if there are none
- if self.servicetags:
- for tag in self.servicetags:
- self.log_info("Enabled ServicesLocator: %s" % (tag,))
- else:
- 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 _parseServiceInfo(self, vhostname, recordname, record):
-
- # Extract some useful attributes
- recordguid = record[dsattributes.kDS1AttrGeneratedUID]
- recordlocation = "%s/Computers/%s" % (record[dsattributes.kDSNAttrMetaNodeLocation], recordname)
-
- # First check for apple-serviceinfo attribute
- plist = record.get("dsAttrTypeNative:apple-serviceinfo", None)
- if not plist:
- return False
-
- # Parse the plist and look for our special entry
- plist = readPlistFromString(plist)
- vhosts = plist.get("com.apple.macosxserver.virtualhosts", None)
- if not vhosts:
- self.log_error(
- "Open Directory (node=%s) %s record does not have a "
- "com.apple.macosxserver.virtualhosts in its apple-serviceinfo attribute value"
- % (self.realmName, recordlocation)
- )
- 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:
- # We can get false positives from the query - we ignore those.
- return False
-
- # Get host name
- hostname = vhosts[hostguid].get("hostname", None)
- if not hostname:
- self.log_error(
- "Open Directory (node=%s) %s record does not have "
- "any host name in its apple-serviceinfo attribute value"
- % (self.realmName, recordlocation)
- )
- return False
- if hostname != vhostname:
- # We can get false positives from the query - we ignore those.
- return False
-
- # Get host details. At this point we only check that it is present. We actually
- # ignore the details themselves (scheme/port) as we use our own config for that.
- hostdetails = vhosts[hostguid].get("hostDetails", None)
- if not hostdetails:
- self.log_error(
- "Open Directory (node=%s) %s record does not have "
- "any host details in its apple-serviceinfo attribute value"
- % (self.realmName, recordlocation)
- )
- return False
-
- # Look at the service data
- serviceInfos = vhosts[hostguid].get("serviceInfo", None)
- if not serviceInfos or not serviceInfos.has_key("calendar"):
- self.log_error(
- "Open Directory (node=%s) %s record does not have a "
- "calendar service in its apple-serviceinfo attribute value"
- % (self.realmName, recordlocation)
- )
- return False
- serviceInfo = serviceInfos["calendar"]
-
- # Check that this service is enabled
- enabled = serviceInfo.get("enabled", True)
-
- # Create the string we will use to match users with accounts on this server
- servicetag = "%s:%s:calendar" % (recordguid, hostguid)
-
- self.computerRecords[recordlocation] = (recordname, enabled, servicetag)
-
- if enabled:
- self.servicetags.add(servicetag)
-
- return True
-
- def _calendarUserAddresses(self, recordType, recordName, record):
- """
- Extract specific attributes from the directory record for use as calendar user address.
-
- @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.
- """
- # Now get the addresses
- result = set()
-
- # Add each email address as a mailto URI
- emails = record.get(dsattributes.kDSNAttrEMailAddress)
- if emails is not None:
- if isinstance(emails, str):
- emails = [emails]
- for email in emails:
- result.add("mailto:%s" % (email.lower(),))
-
- return result
-
- def _parseResourceInfo(self, plist, guid, recordType, shortname):
- """
- Parse OD ResourceInfo attribute and extract information that the server needs.
-
- @param plist: the plist that is the attribute value.
- @type plist: str
- @param guid: the directory GUID of the record being parsed.
- @type guid: str
- @param shortname: the record shortname of the record being parsed.
- @type shortname: str
- @return: a C{tuple} of C{bool} for auto-accept, C{str} for proxy GUID, C{str} for read-only proxy GUID.
- """
- try:
- plist = readPlistFromString(plist)
- wpframework = plist.get("com.apple.WhitePagesFramework", {})
- autoaccept = wpframework.get("AutoAcceptsInvitation", False)
- proxy = wpframework.get("CalendaringDelegate", None)
- read_only_proxy = wpframework.get("ReadOnlyCalendaringDelegate", None)
- except (ExpatError, AttributeError), e:
- self.log_error(
- "Failed to parse ResourceInfo attribute of record (%s)%s (guid=%s): %s\n%s" %
- (recordType, shortname, guid, e, plist,)
- )
- raise ValueError("Invalid ResourceInfo")
-
- return (autoaccept, proxy, read_only_proxy,)
-
def recordTypes(self):
return (
DirectoryService.recordType_users,
@@ -616,9 +381,6 @@
if recordType == DirectoryService.recordType_groups:
groupsForGUID = {}
- elif recordType in (DirectoryService.recordType_resources, DirectoryService.recordType_locations):
- proxiesForGUID = {}
- readOnlyProxiesForGUID = {}
else:
storage = self._records[recordType]
@@ -631,60 +393,22 @@
if recordType == DirectoryService.recordType_groups:
groupsForGUID = storage["groupsForGUID"]
- elif recordType in (DirectoryService.recordType_resources, DirectoryService.recordType_locations):
- proxiesForGUID = storage["proxiesForGUID"]
- readOnlyProxiesForGUID = storage["readOnlyProxiesForGUID"]
- for (recordShortName, value) in results:
- enabledForCalendaring = True
-
- if self.requireComputerRecord:
- servicesLocators = value.get(dsattributes.kDSNAttrServicesLocator)
-
- def allowForACLs():
- return recordType in (
- DirectoryService.recordType_users,
- DirectoryService.recordType_groups,
- )
-
- def disableForCalendaring():
- self.log_debug(
- "Record (%s) %s is not enabled for calendaring but may be used in ACLs"
- % (recordType, recordShortName)
- )
-
- def invalidRecord():
- self.log_error(
- "Directory (incorrectly) returned a record with no applicable "
- "ServicesLocator attribute: (%s) %s"
- % (recordType, recordShortName)
- )
-
- if servicesLocators:
- if type(servicesLocators) is str:
- servicesLocators = (servicesLocators,)
-
- for locator in servicesLocators:
- if locator in self.servicetags:
- break
- else:
- if allowForACLs():
- disableForCalendaring()
- enabledForCalendaring = False
- else:
- invalidRecord()
- continue
+ def _setFromAttribute(attribute, lower=False):
+ if attribute:
+ if isinstance(attribute, str):
+ return set((attribute.lower() if lower else attribute,))
else:
- if allowForACLs():
- disableForCalendaring()
- enabledForCalendaring = False
- else:
- invalidRecord()
- continue
+ return set([item.lower() if lower else item for item in attribute])
+ else:
+ return ()
+ for (recordShortName, value) in results:
+
# Now get useful record info.
recordGUID = value.get(dsattributes.kDS1AttrGeneratedUID)
recordFullName = value.get(dsattributes.kDS1AttrDistinguishedName)
+ recordEmailAddresses = _setFromAttribute(value.get(dsattributes.kDSNAttrEMailAddress), lower=True)
recordNodeName = value.get(dsattributes.kDSNAttrMetaNodeLocation)
if not recordGUID:
@@ -692,12 +416,6 @@
% (recordType, recordShortName, recordNodeName))
continue
- # Get calendar user addresses from directory record.
- if enabledForCalendaring:
- calendarUserAddresses = self._calendarUserAddresses(recordType, recordShortName, value)
- else:
- calendarUserAddresses = ()
-
# Special case for groups, which have members.
if recordType == DirectoryService.recordType_groups:
memberGUIDs = value.get(dsattributes.kDSNAttrGroupMembers)
@@ -713,22 +431,6 @@
else:
memberGUIDs = ()
- # Special case for resources and locations
- autoSchedule = False
- proxyGUIDs = ()
- readOnlyProxyGUIDs = ()
- if recordType in (DirectoryService.recordType_resources, DirectoryService.recordType_locations):
- resourceInfo = value.get(dsattributes.kDSNAttrResourceInfo)
- if resourceInfo is not None:
- try:
- autoSchedule, proxy, read_only_proxy = self._parseResourceInfo(resourceInfo, recordGUID, recordType, recordShortName)
- except ValueError:
- continue
- if proxy:
- proxyGUIDs = (proxy,)
- if read_only_proxy:
- readOnlyProxyGUIDs = (read_only_proxy,)
-
record = OpenDirectoryRecord(
service = self,
recordType = recordType,
@@ -736,14 +438,16 @@
nodeName = recordNodeName,
shortName = recordShortName,
fullName = recordFullName,
- calendarUserAddresses = calendarUserAddresses,
- autoSchedule = autoSchedule,
- enabledForCalendaring = enabledForCalendaring,
+ emailAddresses = recordEmailAddresses,
memberGUIDs = memberGUIDs,
- proxyGUIDs = proxyGUIDs,
- readOnlyProxyGUIDs = readOnlyProxyGUIDs,
)
+ # 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.
+ d = augment.AugmentService.getAugmentRecord(record.guid)
+ d.addCallback(lambda x:record.addAugmentInformation(x))
+
def disableRecord(record):
self.log_warn("Record disabled due to conflict (record name and GUID must match): %s" % (record,))
@@ -789,11 +493,6 @@
if recordType == DirectoryService.recordType_groups:
self._indexGroup(record, record._memberGUIDs, groupsForGUID)
- # Do proxy indexing if needed
- elif recordType in (DirectoryService.recordType_resources, DirectoryService.recordType_locations):
- self._indexGroup(record, record._proxyGUIDs, proxiesForGUID)
- self._indexGroup(record, record._readOnlyProxyGUIDs, readOnlyProxiesForGUID)
-
if shortName is None and guid is None:
#
# Replace the entire cache
@@ -811,11 +510,6 @@
if recordType == DirectoryService.recordType_groups:
storage["groupsForGUID"] = groupsForGUID
- # Add proxy indexing if needed
- elif recordType in (DirectoryService.recordType_resources, DirectoryService.recordType_locations):
- storage["proxiesForGUID"] = proxiesForGUID
- storage["readOnlyProxiesForGUID"] = readOnlyProxiesForGUID
-
def rot():
storage["status"] = "stale"
removals = set()
@@ -861,80 +555,13 @@
elif recordType == DirectoryService.recordType_locations:
listRecordType = dsattributes.kDSStdRecordTypePlaces
- attrs.append(dsattributes.kDSNAttrResourceInfo)
elif recordType == DirectoryService.recordType_resources:
listRecordType = dsattributes.kDSStdRecordTypeResources
- attrs.append(dsattributes.kDSNAttrResourceInfo)
else:
raise UnknownRecordTypeError("Unknown Open Directory record type: %s" % (recordType))
- if self.requireComputerRecord:
- if self.isWorkgroupServer and recordType == DirectoryService.recordType_users:
- if shortName is None and guid is None:
- self.log_debug("opendirectory.queryRecordsWithAttribute_list(%r,%r,%r,%r,%r,%r,%r)" % (
- self.directory,
- dsattributes.kDSNAttrRecordName,
- saclGroup,
- dsattributes.eDSExact,
- False,
- dsattributes.kDSStdRecordTypeGroups,
- [dsattributes.kDSNAttrGroupMembers, dsattributes.kDSNAttrNestedGroups],
- ))
- results = opendirectory.queryRecordsWithAttribute_list(
- self.directory,
- dsattributes.kDSNAttrRecordName,
- saclGroup,
- dsattributes.eDSExact,
- False,
- dsattributes.kDSStdRecordTypeGroups,
- [dsattributes.kDSNAttrGroupMembers, dsattributes.kDSNAttrNestedGroups]
- )
-
- if len(results) == 1:
- members = results[0][1].get(dsattributes.kDSNAttrGroupMembers, [])
- nestedGroups = results[0][1].get(dsattributes.kDSNAttrNestedGroups, [])
- else:
- members = []
- nestedGroups = []
-
- guidQueries = []
-
- for GUID in self._expandGroupMembership(members, nestedGroups):
- guidQueries.append(
- dsquery.match(dsattributes.kDS1AttrGeneratedUID, GUID, dsattributes.eDSExact)
- )
-
- if not guidQueries:
- self.log_warn("No SACL enabled users found.")
- return ()
-
- query = dsquery.expression(dsquery.expression.OR, guidQueries)
-
- #
- # For users and groups, we'll load all entries, even if
- # they don't have a services locator for this server.
- #
- elif (
- recordType != DirectoryService.recordType_users and
- recordType != DirectoryService.recordType_groups
- ):
- tag_queries = []
-
- for tag in self.servicetags:
- tag_queries.append(dsquery.match(dsattributes.kDSNAttrServicesLocator, tag, dsattributes.eDSExact))
-
- if len(tag_queries) == 1:
- subquery = tag_queries[0]
- else:
- subquery = dsquery.expression(dsquery.expression.OR, tag_queries)
-
- if query is None:
- query = subquery
- else:
- query = dsquery.expression(dsquery.expression.AND, (subquery, query))
-
if shortName is not None:
subquery = dsquery.match(dsattributes.kDSNAttrRecordName, shortName, dsattributes.eDSExact)
elif guid is not None:
@@ -1007,8 +634,7 @@
"""
def __init__(
self, service, recordType, guid, nodeName, shortName, fullName,
- calendarUserAddresses, autoSchedule, enabledForCalendaring,
- memberGUIDs, proxyGUIDs, readOnlyProxyGUIDs,
+ emailAddresses, memberGUIDs,
):
super(OpenDirectoryRecord, self).__init__(
service = service,
@@ -1016,14 +642,10 @@
guid = guid,
shortName = shortName,
fullName = fullName,
- calendarUserAddresses = calendarUserAddresses,
- autoSchedule = autoSchedule,
- enabledForCalendaring = enabledForCalendaring,
+ emailAddresses = emailAddresses,
)
self.nodeName = nodeName
self._memberGUIDs = tuple(memberGUIDs)
- self._proxyGUIDs = tuple(proxyGUIDs)
- self._readOnlyProxyGUIDs = tuple(readOnlyProxyGUIDs)
def __repr__(self):
if self.service.realmName == self.nodeName:
@@ -1053,40 +675,6 @@
def groups(self):
return self.service.groupsForGUID(self.guid)
- def proxies(self):
- if self.recordType not in (DirectoryService.recordType_resources, DirectoryService.recordType_locations):
- return
-
- for guid in self._proxyGUIDs:
- proxyRecord = self.service.recordWithGUID(guid)
- if proxyRecord is None:
- self.log_error("No record for proxy in %s with GUID %s" % (self.shortName, guid))
- else:
- yield proxyRecord
-
- def proxyFor(self):
- result = set()
- result.update(self.service.proxiesForGUID(DirectoryService.recordType_resources, self.guid))
- result.update(self.service.proxiesForGUID(DirectoryService.recordType_locations, self.guid))
- return result
-
- def readOnlyProxies(self):
- if self.recordType not in (DirectoryService.recordType_resources, DirectoryService.recordType_locations):
- return
-
- for guid in self._readOnlyProxyGUIDs:
- proxyRecord = self.service.recordWithGUID(guid)
- if proxyRecord is None:
- self.log_error("No record for proxy in %s with GUID %s" % (self.shortName, guid))
- else:
- yield proxyRecord
-
- def readOnlyProxyFor(self):
- result = set()
- result.update(self.service.readOnlyProxiesForGUID(DirectoryService.recordType_resources, self.guid))
- result.update(self.service.readOnlyProxiesForGUID(DirectoryService.recordType_locations, self.guid))
- return result
-
def verifyCredentials(self, credentials):
if isinstance(credentials, UsernamePassword):
# Check cached password
Added: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/augment.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/augment.py (rev 0)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/augment.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -0,0 +1,285 @@
+##
+# Copyright (c) 2009 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.
+##
+
+
+from twisted.internet.defer import inlineCallbacks, returnValue, succeed
+from twistedcaldav.database import AbstractADBAPIDatabase
+from twistedcaldav.directory.xmlaugmentsparser import XMLAugmentsParser
+import time
+
+from twistedcaldav.log import Logger
+
+log = Logger()
+
+class AugmentRecord(object):
+ """
+ Augmented directory record information
+ """
+
+ def __init__(
+ self,
+ guid,
+ enabled=False,
+ hostedAt="",
+ enabledForCalendaring=False,
+ autoSchedule=False,
+ calendarUserAddresses=None,
+ ):
+ self.guid = guid
+ self.enabled = enabled
+ self.hostedAt = hostedAt
+ self.enabledForCalendaring = enabledForCalendaring
+ self.autoSchedule = autoSchedule
+ self.calendarUserAddresses = calendarUserAddresses if calendarUserAddresses else set()
+
+class AugmentDB(object):
+ """
+ Abstract base class for an augment record database.
+ """
+
+ def __init__(self):
+ pass
+
+ def getAugmentRecord(self, guid):
+ """
+ Get an AugmentRecord for the specified GUID.
+
+ @param guid: directory GUID to lookup
+ @type guid: C{str}
+
+ @return: L{Deferred}
+ """
+
+ raise NotImplementedError("Child class must define this.")
+
+ def refresh(self):
+ """
+ Refresh any cached data.
+ """
+ pass
+
+AugmentService = AugmentDB() # Global augment service
+
+
+class AugmentXMLDB(AugmentDB):
+ """
+ XMLFile based augment database implementation.
+ """
+
+ def __init__(self, xmlFiles, cacheTimeout=30):
+
+ self.xmlFiles = xmlFiles
+ self.cacheTimeout = cacheTimeout * 60 # Value is mins we want secs
+ self.lastCached = 0
+ self.db = {}
+
+ try:
+ self.db = self._parseXML()
+ except RuntimeError:
+ log.error("Failed to parse XML augments file - fatal error on startup")
+ raise
+
+ self.lastCached = time.time()
+
+ def getAugmentRecord(self, guid):
+ """
+ Get an AugmentRecord for the specified GUID.
+
+ @param guid: directory GUID to lookup
+ @type guid: C{str}
+
+ @return: L{Deferred}
+ """
+
+ # May need to re-cache
+ if self.lastCached + self.cacheTimeout <= time.time():
+ self.refresh()
+
+ return succeed(self.db.get(guid))
+
+ def refresh(self):
+ """
+ Refresh any cached data.
+ """
+ try:
+ self.db = self._parseXML()
+ except RuntimeError:
+ log.error("Failed to parse XML augments file during cache refresh - ignoring")
+ self.lastCached = time.time()
+
+ def _parseXML(self):
+
+ # Do each file
+ results = {}
+ for xmlFile in self.xmlFiles:
+
+ # Creating a parser does the parse
+ XMLAugmentsParser(xmlFile, results)
+
+ return results
+
+class AugmentADAPI(AugmentDB, AbstractADBAPIDatabase):
+ """
+ DBAPI based augment database implementation.
+ """
+
+ schema_version = "1"
+ schema_type = "AugmentDB"
+
+ def __init__(self, dbID, dbapiName, dbapiArgs, **kwargs):
+
+ self.cachedPartitions = {}
+ self.cachedHostedAt = {}
+
+ AbstractADBAPIDatabase.__init__(self, dbID, dbapiName, dbapiArgs, True, **kwargs)
+
+ @inlineCallbacks
+ def getAugmentRecord(self, guid):
+ """
+ Get an AugmentRecord for the specified GUID.
+
+ @param guid: directory GUID to lookup
+ @type guid: C{str}
+
+ @return: L{Deferred}
+ """
+
+ # Query for the record information
+ results = (yield self.query("select GUID, ENABLED, PARTITIONID, CALENDARING, AUTOSCHEDULE, CUADDRS from AUGMENTS where GUID = :1", (guid,)))
+ if not results:
+ returnValue(None)
+ else:
+ guid, enabled, partitionid, enabdledForCalendaring, autoSchedule, cuaddrs = results[0]
+
+ record = AugmentRecord(
+ guid = guid,
+ enabled = enabled == "T",
+ hostedAt = (yield self._getPartition(partitionid)),
+ enabledForCalendaring = enabdledForCalendaring == "T",
+ autoSchedule = autoSchedule == "T",
+ calendarUserAddresses = set(cuaddrs.split("\t")) if cuaddrs else set(),
+ )
+
+ returnValue(record)
+
+ @inlineCallbacks
+ def addAugmentRecord(self, record):
+
+ partitionid = (yield self._getPartitionID(record.hostedAt))
+ cuaddrs = "\t".join(record.calendarUserAddresses)
+
+ yield self.execute(
+ """insert into AUGMENTS
+ (GUID, ENABLED, PARTITIONID, CALENDARING, AUTOSCHEDULE, CUADDRS)
+ values (:1, :2, :3, :4, :5, :6)""",
+ (
+ record.guid,
+ "T" if record.enabled else "F",
+ partitionid,
+ "T" if record.enabledForCalendaring else "F",
+ "T" if record.autoSchedule else "F",
+ cuaddrs,)
+ )
+
+ @inlineCallbacks
+ def _getPartitionID(self, hostedat, createIfMissing=True):
+
+ # We will use a cache for these as we do not expect changes whilst running
+ try:
+ returnValue(self.cachedHostedAt[hostedat])
+ except KeyError:
+ pass
+
+ partitionid = (yield self.queryOne("select PARTITIONID from PARTITIONS where HOSTEDAT = :1", (hostedat,)))
+ if partitionid == None:
+ yield self.execute("insert into PARTITIONS (HOSTEDAT) values (:1)", (hostedat,))
+ partitionid = (yield self.queryOne("select PARTITIONID from PARTITIONS where HOSTEDAT = :1", (hostedat,)))
+ self.cachedHostedAt[hostedat] = partitionid
+ returnValue(partitionid)
+
+ @inlineCallbacks
+ def _getPartition(self, partitionid):
+
+ # We will use a cache for these as we do not expect changes whilst running
+ try:
+ returnValue(self.cachedPartitions[partitionid])
+ except KeyError:
+ pass
+
+ partition = (yield self.queryOne("select HOSTEDAT from PARTITIONS where PARTITIONID = :1", (partitionid,)))
+ self.cachedPartitions[partitionid] = partition
+ returnValue(partition)
+
+ @inlineCallbacks
+ def _getCUAddrs(self, augmentid):
+
+ return self.queryList("select CUADDR from CUADDRS where AUGMENTID = :1", (augmentid,))
+
+ def _db_version(self):
+ """
+ @return: the schema version assigned to this index.
+ """
+ return AugmentADAPI.schema_version
+
+ def _db_type(self):
+ """
+ @return: the collection type assigned to this index.
+ """
+ return AugmentADAPI.schema_type
+
+ @inlineCallbacks
+ def _db_init_data_tables(self):
+ """
+ Initialise the underlying database tables.
+ """
+
+ #
+ # TESTTYPE table
+ #
+ yield self._db_execute(
+ """
+ create table AUGMENTS (
+ GUID text unique,
+ ENABLED text(1),
+ PARTITIONID text,
+ CALENDARING text(1),
+ AUTOSCHEDULE text(1),
+ CUADDRS text
+ )
+ """
+ )
+ yield self._db_execute(
+ """
+ create table PARTITIONS (
+ PARTITIONID integer primary key autoincrement,
+ HOSTEDAT text
+ )
+ """
+ )
+
+ @inlineCallbacks
+ def _db_remove_data_tables(self):
+ yield self._db_execute("drop table if exists AUGMENTS")
+ yield self._db_execute("drop table if exists PARTITIONS")
+
+class AugmentSqliteDB(AugmentADAPI):
+ """
+ Sqlite based augment database implementation.
+ """
+
+ def __init__(self, dbpath):
+
+ super(AugmentSqliteDB, self).__init__("Augments", "sqlite3", (dbpath,))
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/calendaruserproxy.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/calendaruserproxy.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/calendaruserproxy.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -1,5 +1,5 @@
##
-# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+# Copyright (c) 2006-2009 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.
@@ -38,9 +38,11 @@
from twistedcaldav.resource import CalDAVComplianceMixIn
from twistedcaldav.directory.util import NotFilePath
from twistedcaldav.sql import AbstractSQLDatabase
+from twistedcaldav.log import LoggingMixIn
import itertools
import os
+import time
class PermissionsMixIn (ReadOnlyWritePropertiesResourceMixIn):
def defaultAccessControlList(self):
@@ -69,7 +71,7 @@
return davxml.ACL(*aces)
def accessControlList(self, request, inheritance=True, expanding=False, inherited_aces=None):
- # Permissions here are fixed, and are not subject to inherritance rules, etc.
+ # Permissions here are fixed, and are not subject to inheritance rules, etc.
return succeed(self.defaultAccessControlList())
class CalendarUserProxyPrincipalResource (CalDAVComplianceMixIn, PermissionsMixIn, DAVPrincipalResource, DAVFile):
@@ -156,15 +158,7 @@
assert isinstance(property, davxml.WebDAVElement)
if property.qname() == (dav_namespace, "group-member-set"):
- if self.hasEditableMembership():
- return self.setGroupMemberSet(property, request)
- else:
- raise HTTPError(
- StatusResponse(
- responsecode.FORBIDDEN,
- "Proxies cannot be changed."
- )
- )
+ return self.setGroupMemberSet(property, request)
return super(CalendarUserProxyPrincipalResource, self).writeProperty(property, request)
@@ -182,8 +176,9 @@
# Break out the list into a set of URIs.
members = [str(h) for h in new_members.children]
- # Map the URIs to principals.
+ # Map the URIs to principals and a set of UIDs.
principals = []
+ newUIDs = set()
for uri in members:
principal = self.pcollection._principalForURI(uri)
# Invalid principals MUST result in an error.
@@ -193,14 +188,31 @@
"Attempt to use a non-existent principal %s as a group member of %s." % (uri, self.principalURL(),)
))
principals.append(principal)
- yield principal.cacheNotifier.changed()
+ newUIDs.add(principal.principalUID())
+ # Get the old set of UIDs
+ oldUIDs = (yield self._index().getMembers(self.uid))
+
+ # Change membership
+ yield self.setGroupMemberSetPrincipals(principals)
+
+ # Invalidate the primary principal's cache, and any principal's whose
+ # membership status changed
+ yield self.parent.cacheNotifier.changed()
+
+ changedUIDs = newUIDs.symmetric_difference(oldUIDs)
+ for uid in changedUIDs:
+ principal = self.pcollection.principalForUID(uid)
+ if principal:
+ yield principal.cacheNotifier.changed()
+
+ returnValue(True)
+
+ @inlineCallbacks
+ def setGroupMemberSetPrincipals(self, principals):
# Map the principals to UIDs.
uids = [p.principalUID() for p in principals]
-
yield self._index().setGroupMembers(self.uid, uids)
- yield self.parent.cacheNotifier.changed()
- returnValue(True)
##
# HTTP
@@ -246,8 +258,7 @@
"""Principal UID: %s\n""" % (self.principalUID(),),
"""Principal URL: %s\n""" % (format_link(self.principalURL()),),
"""\nAlternate URIs:\n""" , format_list(format_link(u) for u in self.alternateURIs()),
- """\nGroup members (%s):\n""" % ({False:"Locked", True:"Editable"}[self.hasEditableMembership()])
- , format_principals(closure["members"]),
+ """\nGroup members:\n""" , format_principals(closure["members"]),
"""\nGroup memberships:\n""" , format_principals(closure["memberships"]),
"""</pre></blockquote></div>""",
closure["output"]
@@ -307,17 +318,29 @@
@inlineCallbacks
def _directGroupMembers(self):
- if self.hasEditableMembership():
- # Get member UIDs from database and map to principal resources
- members = yield self._index().getMembers(self.uid)
- returnValue([p for p in [self.pcollection.principalForUID(uid) for uid in members] if p])
- else:
- # Fixed proxies
- if self.proxyType == "calendar-proxy-write":
- returnValue(self.parent.proxies())
+ # Get member UIDs from database and map to principal resources
+ members = yield self._index().getMembers(self.uid)
+ found = []
+ missing = []
+ for uid in members:
+ p = self.pcollection.principalForUID(uid)
+ if p:
+ found.append(p)
+ # Make sure any outstanding deletion timer entries for
+ # existing principals are removed
+ yield self._index()._memcacher.clearDeletionTimer(uid)
else:
- returnValue(self.parent.readOnlyProxies())
+ missing.append(uid)
+ # Clean-up ones that are missing
+ for uid in missing:
+ cacheTimeout = config.DirectoryService.params.get("cacheTimeout", 30) * 60 # in seconds
+
+ yield self._index().removePrincipal(uid,
+ delay=cacheTimeout*2)
+
+ returnValue(found)
+
def groupMembers(self):
return self._expandMemberUIDs()
@@ -327,10 +350,7 @@
memberships = yield self._index().getMemberships(self.uid)
returnValue([p for p in [self.pcollection.principalForUID(uid) for uid in memberships] if p])
- def hasEditableMembership(self):
- return self.parent.hasEditableProxyMembership()
-
-class CalendarUserProxyDatabase(AbstractSQLDatabase):
+class CalendarUserProxyDatabase(AbstractSQLDatabase, LoggingMixIn):
"""
A database to maintain calendar user proxy group memberships.
@@ -384,6 +404,33 @@
def deleteMembership(self, guid):
return self.delete("memberships:%s" % (guid,))
+ def setDeletionTimer(self, guid, delay):
+ return self.set("del:%s" % (str(guid),), str(self.getTime()+delay))
+
+ def checkDeletionTimer(self, guid):
+ # True means it's overdue, False means it's not, None means no timer
+ def _value(value):
+ if value:
+ if int(value) <= self.getTime():
+ return True
+ else:
+ return False
+ else:
+ return None
+ d = self.get("del:%s" % (str(guid),))
+ d.addCallback(_value)
+ return d
+
+ def clearDeletionTimer(self, guid):
+ return self.delete("del:%s" % (str(guid),))
+
+ def getTime(self):
+ if hasattr(self, 'theTime'):
+ theTime = self.theTime
+ else:
+ theTime = int(time.time())
+ return theTime
+
def __init__(self, path):
path = os.path.join(path, CalendarUserProxyDatabase.dbFilename)
super(CalendarUserProxyDatabase, self).__init__(path, True)
@@ -405,11 +452,8 @@
current_members = ()
current_members = set(current_members)
- # Remove what is there, then add it back.
- self._delete_from_db(principalUID)
- self._add_to_db(principalUID, members)
- self._db_commit()
-
+ self.setGroupMembersInDatabase(principalUID, members)
+
# Update cache
update_members = set(members)
@@ -419,6 +463,18 @@
_ignore = yield self._memcacher.deleteMembership(member)
_ignore = yield self._memcacher.deleteMember(principalUID)
+ def setGroupMembersInDatabase(self, principalUID, members):
+ """
+ A blocking call to add a group membership record in the database.
+
+ @param principalUID: the UID of the group principal to add.
+ @param members: a list UIDs of principals that are members of this group.
+ """
+ # Remove what is there, then add it back.
+ self._delete_from_db(principalUID)
+ self._add_to_db(principalUID, members)
+ self._db_commit()
+
@inlineCallbacks
def removeGroup(self, principalUID):
"""
@@ -427,17 +483,71 @@
@param principalUID: the UID of the group principal to remove.
"""
+ # Need to get the members before we do the delete
+ members = yield self.getMembers(principalUID)
+
self._delete_from_db(principalUID)
self._db_commit()
# Update cache
- members = yield self.getMembers(principalUID)
if members:
for member in members:
yield self._memcacher.deleteMembership(member)
yield self._memcacher.deleteMember(principalUID)
@inlineCallbacks
+ def removePrincipal(self, principalUID, delay=None):
+ """
+ Remove a group membership record.
+
+ @param principalUID: the UID of the principal to remove.
+ """
+
+ if delay:
+ # We are going to remove the principal only after <delay> seconds
+ # has passed since we first chose to remove it, to protect against
+ # transient directory problems.
+ # If <delay> is specified, first see if there was a timer set
+ # previously. If the timer is more than delay seconds old, we
+ # go ahead and remove the principal. Otherwise, do nothing.
+
+ overdue = yield self._memcacher.checkDeletionTimer(principalUID)
+
+ if overdue == False:
+ # Do nothing
+ returnValue(None)
+
+ elif overdue is None:
+ # No timer was previously set
+ self.log_debug("Delaying removal of missing proxy principal '%s'" %
+ (principalUID,))
+ self._memcacher.setDeletionTimer(principalUID, delay=delay)
+ returnValue(None)
+
+ self.log_warn("Removing missing proxy principal for '%s'" %
+ (principalUID,))
+
+ for suffix in ("calendar-proxy-read", "calendar-proxy-write",):
+ groupUID = "%s#%s" % (principalUID, suffix,)
+ self._delete_from_db(groupUID)
+
+ # Update cache
+ members = yield self.getMembers(groupUID)
+ if members:
+ for member in members:
+ yield self._memcacher.deleteMembership(member)
+ yield self._memcacher.deleteMember(groupUID)
+
+ memberships = (yield self.getMemberships(principalUID))
+ for groupUID in memberships:
+ yield self._memcacher.deleteMember(groupUID)
+
+ self._delete_from_db_member(principalUID)
+ yield self._memcacher.deleteMembership(principalUID)
+ self._db_commit()
+ self._memcacher.clearDeletionTimer(principalUID)
+
+ @inlineCallbacks
def getMembers(self, principalUID):
"""
Return the list of group member UIDs for the specified principal.
@@ -502,6 +612,14 @@
"""
self._db_execute("delete from GROUPS where GROUPNAME = :1", principalUID)
+ def _delete_from_db_member(self, principalUID):
+ """
+ Deletes the specified member entry from the database.
+
+ @param principalUID: the UID of the member principal to remove.
+ """
+ self._db_execute("delete from GROUPS where MEMBER = :1", principalUID)
+
def _db_version(self):
"""
@return: the schema version assigned to this index.
Added: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/calendaruserproxyloader.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/calendaruserproxyloader.py (rev 0)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/calendaruserproxyloader.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -0,0 +1,138 @@
+##
+# Copyright (c) 2009 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.
+##
+
+from xml.etree.ElementTree import ElementTree
+from xml.parsers.expat import ExpatError
+from twistedcaldav.directory.calendaruserproxy import CalendarUserProxyDatabase
+from twistedcaldav.config import config
+from twisted.internet.defer import inlineCallbacks
+import types
+
+"""
+XML based calendar user proxy loader.
+"""
+
+__all__ = [
+ "XMLCalendarUserProxyLoader",
+]
+
+from twistedcaldav.log import Logger
+
+log = Logger()
+
+ELEMENT_PROXIES = "proxies"
+ELEMENT_RECORD = "record"
+
+ELEMENT_GUID = "guid"
+ELEMENT_PROXIES = "proxies"
+ELEMENT_READ_ONLY_PROXIES = "read-only-proxies"
+ELEMENT_MEMBER = "member"
+
+ATTRIBUTE_REPEAT = "repeat"
+
+class XMLCalendarUserProxyLoader(object):
+ """
+ XML calendar user proxy configuration file parser and loader.
+ """
+ def __repr__(self):
+ return "<%s %r>" % (self.__class__.__name__, self.xmlFile)
+
+ def __init__(self, xmlFile):
+
+ self.items = []
+ self.xmlFile = xmlFile
+
+ # Read in XML
+ try:
+ tree = ElementTree(file=self.xmlFile)
+ except ExpatError, e:
+ log.error("Unable to parse file '%s' because: %s" % (self.xmlFile, e,), raiseException=RuntimeError)
+
+ # Verify that top-level element is correct
+ proxies_node = tree.getroot()
+ if proxies_node.tag != ELEMENT_PROXIES:
+ log.error("Ignoring file '%s' because it is not a proxies file" % (self.xmlFile,), raiseException=RuntimeError)
+
+ self._parseXML(proxies_node)
+
+ def _parseXML(self, rootnode):
+ """
+ Parse the XML root node from the augments configuration document.
+ @param rootnode: the L{Element} to parse.
+ """
+ for child in rootnode.getchildren():
+
+ if child.tag != ELEMENT_RECORD:
+ log.error("Unknown augment type: '%s' in augment file: '%s'" % (child.tag, self.xmlFile,), raiseException=RuntimeError)
+
+ repeat = int(child.get(ATTRIBUTE_REPEAT, "1"))
+
+ guid = None
+ write_proxies = set()
+ read_proxies = set()
+ for node in child.getchildren():
+
+ if node.tag == ELEMENT_GUID:
+ guid = node.text
+
+ elif node.tag in (
+ ELEMENT_PROXIES,
+ ELEMENT_READ_ONLY_PROXIES,
+ ):
+ self._parseMembers(node, write_proxies if node.tag == ELEMENT_PROXIES else read_proxies)
+ else:
+ log.error("Invalid element '%s' in proxies file: '%s'" % (node.tag, self.xmlFile,), raiseException=RuntimeError)
+
+ # Must have at least a guid
+ if not guid:
+ log.error("Invalid record '%s' without a guid in proxies file: '%s'" % (child, self.xmlFile,), raiseException=RuntimeError)
+
+ if repeat > 1:
+ for i in xrange(1, repeat+1):
+ self._buildRecord(guid, write_proxies, read_proxies, i)
+ else:
+ self._buildRecord(guid, write_proxies, read_proxies)
+
+ def _parseMembers(self, node, addto):
+ for child in node.getchildren():
+ if child.tag == ELEMENT_MEMBER:
+ addto.add(child.text)
+
+ def _buildRecord(self, guid, write_proxies, read_proxies, count=None):
+
+ def expandCount(value, count):
+
+ if type(value) in types.StringTypes:
+ return value % (count,) if count and "%" in value else value
+ else:
+ return value
+
+ guid = expandCount(guid, count)
+ write_proxies = set([expandCount(member, count) for member in write_proxies])
+ read_proxies = set([expandCount(member, count) for member in read_proxies])
+
+ self.items.append((guid, write_proxies, read_proxies,))
+
+ @inlineCallbacks
+ def updateProxyDB(self):
+
+ db = CalendarUserProxyDatabase(config.DataRoot)
+ for item in self.items:
+ guid, write_proxies, read_proxies = item
+ for proxy in write_proxies:
+ yield db.setGroupMembers("%s#%s" % (guid, "calendar-proxy-write"), (proxy,))
+ for proxy in read_proxies:
+ yield db.setGroupMembers("%s#%s" % (guid, "calendar-proxy-read"), (proxy,))
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/directory.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/directory.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/directory.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -34,9 +34,11 @@
from twisted.cred.checkers import ICredentialsChecker
from twisted.web2.dav.auth import IPrincipalCredentials
+from twistedcaldav.config import config
from twistedcaldav.log import LoggingMixIn
from twistedcaldav.directory.idirectory import IDirectoryService, IDirectoryRecord
from twistedcaldav.directory.util import uuidFromName
+from twistedcaldav.partitions import partitions
class DirectoryService(LoggingMixIn):
implements(IDirectoryService, ICredentialsChecker)
@@ -144,19 +146,20 @@
implements(IDirectoryRecord)
def __repr__(self):
- return "<%s[%s@%s(%s)] %s(%s) %r>" % (
+ return "<%s[%s@%s(%s)] %s(%s) %r @ %s>" % (
self.__class__.__name__,
self.recordType,
self.service.guid,
self.service.realmName,
self.guid,
self.shortName,
- self.fullName
+ self.fullName,
+ self.hostedAt,
)
def __init__(
- self, service, recordType, guid, shortName, fullName,
- calendarUserAddresses, autoSchedule, enabledForCalendaring=True,
+ self, service, recordType, guid,
+ shortName, fullName, emailAddresses,
):
assert service.realmName is not None
assert recordType
@@ -165,19 +168,17 @@
if not guid:
guid = uuidFromName(service.guid, "%s:%s" % (recordType, shortName))
- if enabledForCalendaring:
- calendarUserAddresses.add("urn:uuid:%s" % (guid,))
- else:
- assert len(calendarUserAddresses) == 0
-
self.service = service
self.recordType = recordType
self.guid = guid
+ self.enabled = False
+ self.hostedAt = ""
self.shortName = shortName
self.fullName = fullName
- self.enabledForCalendaring = enabledForCalendaring
- self.calendarUserAddresses = calendarUserAddresses
- self.autoSchedule = autoSchedule
+ self.emailAddresses = emailAddresses
+ self.enabledForCalendaring = False
+ self.autoSchedule = False
+ self.calendarUserAddresses = set()
def __cmp__(self, other):
if not isinstance(other, DirectoryRecord):
@@ -192,35 +193,51 @@
def __hash__(self):
h = hash(self.__class__)
for attr in ("service", "recordType", "shortName", "guid",
- "enabledForCalendaring"):
+ "enabled", "enabledForCalendaring"):
h = (h + hash(getattr(self, attr))) & sys.maxint
return h
- def members(self):
- return ()
+ def addAugmentInformation(self, augment):
+
+ if augment:
+ self.enabled = augment.enabled
+ self.hostedAt = augment.hostedAt
+ self.enabledForCalendaring = augment.enabledForCalendaring
+ self.autoSchedule = augment.autoSchedule
+ self.calendarUserAddresses = set(augment.calendarUserAddresses)
- def groups(self):
- return ()
+ if self.enabledForCalendaring and self.recordType == self.service.recordType_groups:
+ raise AssertionError("Groups may not be enabled for calendaring")
+
+ if self.enabledForCalendaring:
+ for email in self.emailAddresses:
+ self.calendarUserAddresses.add("mailto:%s" % (email.lower(),))
+ self.calendarUserAddresses.add("urn:uuid:%s" % (self.guid,))
+ else:
+ assert len(self.calendarUserAddresses) == 0
- def proxies(self):
- return ()
+ else:
+ self.enabled = False
+ self.hostedAt = ""
+ self.enabledForCalendaring = False
+ self.calendarUserAddresses = set()
- def proxyFor(self):
+ def members(self):
return ()
- def readOnlyProxies(self):
+ def groups(self):
return ()
- def readOnlyProxyFor(self):
- return ()
-
- def hasEditableProxyMembership(self):
- return self.recordType in (DirectoryService.recordType_users, DirectoryService.recordType_groups)
-
def verifyCredentials(self, credentials):
return False
+ def locallyHosted(self):
+ return not self.hostedAt or not config.EnablePartitions or self.hostedAt == config.ServerPartitionID
+
+ def hostedURL(self):
+ return partitions.getPartitionURL(self.hostedAt)
+
class DirectoryError(RuntimeError):
"""
Generic directory error.
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/idirectory.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/idirectory.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/idirectory.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -79,11 +79,13 @@
service = Attribute("The L{IDirectoryService} this record exists in.")
recordType = Attribute("The type of this record.")
guid = Attribute("The GUID of this record.")
+ enabled = Attribute("Determines whether this record should be provisioned as a principal.")
+ hostedAt = Attribute("Identifies the server that actually hosts data for the record.")
shortName = Attribute("The name of this record.")
fullName = Attribute("The full name of this record.")
+ emailAddress = Attribute("The email address of this record.")
+ enabledForCalendaring = Attribute("Determines whether this record should be provisioned with a calendar home.")
calendarUserAddresses = Attribute("A set of calendar user addresses for this record.")
- autoSchedule = Attribute("Principal identified by this record should automatically accept/deny meetings.")
- enabledForCalendaring = Attribute("Determines whether this record should be provisioned with a calendar home.")
def members():
"""
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/principal.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/principal.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/principal.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -1,5 +1,5 @@
##
-# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+# Copyright (c) 2006-2009 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.
@@ -116,7 +116,7 @@
raise NotImplementedError("Subclass must implement principalForUID()")
def principalForRecord(self, record):
- if record is None:
+ if record is None or not record.enabled:
return None
return self.principalForUID(record.guid)
@@ -145,7 +145,7 @@
return self.getChild(uidsResourceName).getChild(uid)
def _principalForURI(self, uri):
- scheme, netloc, path, params, query, fragment = urlparse(uri)
+ scheme, netloc, path, _ignore_params, _ignore_query, _ignore_fragment = urlparse(uri)
if scheme == "":
pass
@@ -203,7 +203,7 @@
# Next try looking it up in the directory
record = self.directory.recordWithCalendarUserAddress(address)
- if record is not None:
+ if record is not None and record.enabled:
return self.principalForRecord(record)
log.debug("No principal for calendar user address: %r" % (address,))
@@ -275,7 +275,7 @@
def listChildren(self):
if config.EnablePrincipalListings:
- return (record.shortName for record in self.directory.listRecords(self.recordType))
+ return (record.shortName for record in self.directory.listRecords(self.recordType) if record.enabled)
else:
# Not a listable collection
raise HTTPError(responsecode.FORBIDDEN)
@@ -332,7 +332,7 @@
record = self.directory.recordWithGUID(primaryUID)
- if record is None:
+ if record is None or not record.enabled:
log.err("No principal found for UID: %s" % (name,))
return None
@@ -449,6 +449,7 @@
"""Record type: %s\n""" % (self.record.recordType,),
"""Short name: %s\n""" % (self.record.shortName,),
"""Full name: %s\n""" % (self.record.fullName,),
+ """Email addresses:\n""" , format_list(self.record.emailAddresses),
"""Principal UID: %s\n""" % (self.principalUID(),),
"""Principal URL: %s\n""" % (format_link(self.principalURL()),),
"""\nAlternate URIs:\n""" , format_list(format_link(u) for u in self.alternateURIs()),
@@ -512,7 +513,6 @@
if record not in records:
records.add(record)
- myRecordType = self.record.recordType
for relative in getattr(record, method)():
if relative not in records:
found = self.parent.principalForRecord(relative)
@@ -538,10 +538,6 @@
groups = self._getRelatives("groups")
if config.EnableProxyPrincipals:
- # Get any directory specified proxies
- groups.update(self._getRelatives("proxyFor", proxy='read-write'))
- groups.update(self._getRelatives("readOnlyProxyFor", proxy='read-only'))
-
# Get proxy group UIDs and map to principal resources
proxies = []
d = waitForDeferred(self._calendar_user_proxy_index().getMemberships(self.principalUID()))
@@ -551,6 +547,10 @@
subprincipal = self.parent.principalForUID(uid)
if subprincipal:
proxies.append(subprincipal)
+ else:
+ d = waitForDeferred(self._calendar_user_proxy_index().removeGroup(uid))
+ yield d
+ d.getResult()
groups.update(proxies)
@@ -569,13 +569,6 @@
proxyFors.update(results)
if config.EnableProxyPrincipals:
- # Get any directory specified proxies
- if read_write:
- directoryProxies = self._getRelatives("proxyFor", proxy='read-write')
- else:
- directoryProxies = self._getRelatives("readOnlyProxyFor", proxy='read-only')
- proxyFors.update([subprincipal.parent for subprincipal in directoryProxies])
-
# Get proxy group UIDs and map to principal resources
proxies = []
d = waitForDeferred(self._calendar_user_proxy_index().getMemberships(self.principalUID()))
@@ -596,7 +589,23 @@
def principalUID(self):
return self.record.guid
+ def locallyHosted(self):
+ return self.record.locallyHosted()
+
+ def hostedURL(self):
+ return self.record.hostedURL()
+
##
+ # Extra resource info
+ ##
+
+ def setAutoSchedule(self, autoSchedule):
+ self.record.autoSchedule = autoSchedule
+
+ def getAutoSchedule(self):
+ return self.record.autoSchedule
+
+ ##
# Static
##
@@ -699,15 +708,6 @@
def autoSchedule(self):
return self.record.autoSchedule
- def proxies(self):
- return self._getRelatives("proxies")
-
- def readOnlyProxies(self):
- return self._getRelatives("readOnlyProxies")
-
- def hasEditableProxyMembership(self):
- return self.record.hasEditableProxyMembership()
-
def scheduleInbox(self, request):
home = self._calendarHome()
if home is None:
@@ -720,11 +720,8 @@
return succeed(inbox)
def calendarHomeURLs(self):
- home = self._calendarHome()
- if home is None:
- return ()
- else:
- return (home.url(),)
+ homeURL = self._homeChildURL(None)
+ return (homeURL,) if homeURL else ()
def scheduleInboxURL(self):
return self._homeChildURL("inbox/")
@@ -743,7 +740,13 @@
if home is None:
return None
else:
- return joinURL(home.url(), name)
+ url = home.url()
+ if name:
+ url = joinURL(url, name)
+ if not self.locallyHosted():
+ url = joinURL(self.hostedURL(), url)
+
+ return url
def _calendarHome(self):
# FIXME: self.record.service.calendarHomesCollection smells like a hack
Deleted: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/sqldb.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/sqldb.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/sqldb.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -1,377 +0,0 @@
-##
-# Copyright (c) 2006-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.
-##
-
-"""
-SQL (sqlite) based user/group/resource directory service implementation.
-"""
-
-"""
-SCHEMA:
-
-User Database:
-
-ROW: RECORD_TYPE, SHORT_NAME (unique), PASSWORD, NAME
-
-Group Database:
-
-ROW: SHORT_NAME, MEMBER_SHORT_NAME
-
-CUAddress database:
-
-ROW: ADDRESS (unqiue), SHORT_NAME
-
-"""
-
-__all__ = [
- "SQLDirectoryService",
-]
-
-from twisted.cred.credentials import UsernamePassword
-from twisted.python.filepath import FilePath
-
-from twistedcaldav.directory.directory import DirectoryService, DirectoryRecord
-from twistedcaldav.directory.xmlaccountsparser import XMLAccountsParser
-from twistedcaldav.sql import AbstractSQLDatabase
-from twistedcaldav.sql import db_prefix
-
-import os
-
-class SQLDirectoryManager(AbstractSQLDatabase):
- """
- 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_prefix + "accounts"
- dbFormatVersion = "3"
-
- def __init__(self, path):
- path = os.path.join(path, SQLDirectoryManager.dbFilename)
- super(SQLDirectoryManager, self).__init__(path, True)
-
- def loadFromXML(self, xmlFile):
- parser = XMLAccountsParser(xmlFile)
-
- # Totally wipe existing DB and start from scratch
- if os.path.exists(self.dbpath):
- os.remove(self.dbpath)
-
- self._db_execute("insert into SERVICE (REALM) values (:1)", parser.realm)
-
- # Now add records to db
- for item in parser.items.values():
- for entry in item.itervalues():
- self._add_to_db(entry)
- self._db_commit()
-
- def getRealm(self):
- for realm in self._db_execute("select REALM from SERVICE"):
- return realm[0].decode("utf-8")
- else:
- return ""
-
- def listRecords(self, recordType):
- # Get each account record
- for shortName, guid, password, name in self._db_execute(
- """
- select SHORT_NAME, GUID, PASSWORD, NAME
- from ACCOUNTS
- where RECORD_TYPE = :1
- """, recordType
- ):
- # See if we have members
- members = self.members(shortName)
-
- # See if we are a member of any groups
- groups = self.groups(shortName)
-
- # Get calendar user addresses
- calendarUserAddresses = self.calendarUserAddresses(shortName)
-
- # TODO: need this for Resources and Locations
- autoSchedule = False
-
- yield shortName, guid, password, name, members, groups, calendarUserAddresses, autoSchedule
-
- def getRecord(self, recordType, shortName):
- # Get individual account record
- for shortName, guid, password, name in self._db_execute(
- """
- select SHORT_NAME, GUID, PASSWORD, NAME
- from ACCOUNTS
- where RECORD_TYPE = :1
- and SHORT_NAME = :2
- """, recordType, shortName
- ):
- break
- else:
- return None
-
- # See if we have members
- members = self.members(shortName)
-
- # See if we are a member of any groups
- groups = self.groups(shortName)
-
- # Get calendar user addresses
- calendarUserAddresses = self.calendarUserAddresses(shortName)
-
- # TODO: need this for Resources and Locations
- autoSchedule = False
-
- return shortName, guid, password, name, members, groups, calendarUserAddresses, autoSchedule
-
- def members(self, shortName):
- members = set()
- for member in self._db_execute(
- """
- select MEMBER_RECORD_TYPE, MEMBER_SHORT_NAME
- from GROUPS
- where SHORT_NAME = :1
- """, shortName
- ):
- members.add(tuple(member))
- return members
-
- def groups(self, shortName):
- groups = set()
- for (name,) in self._db_execute(
- """
- select SHORT_NAME
- from GROUPS
- where MEMBER_SHORT_NAME = :1
- """, shortName
- ):
- groups.add(name)
- return groups
-
- def calendarUserAddresses(self, shortName):
- calendarUserAddresses = set()
- for (address,) in self._db_execute(
- """
- select ADDRESS
- from ADDRESSES
- where SHORT_NAME = :1
- """, shortName
- ):
- calendarUserAddresses.add(address)
- return calendarUserAddresses
-
- def _add_to_db(self, record):
- # Do regular account entry
- recordType = record.recordType
- shortName = record.shortName
- guid = record.guid
- password = record.password
- name = record.name
-
- self._db_execute(
- """
- insert into ACCOUNTS (RECORD_TYPE, SHORT_NAME, GUID, PASSWORD, NAME)
- values (:1, :2, :3, :4, :5)
- """, recordType, shortName, guid, password, name
- )
-
- # Check for members
- for memberRecordType, memberShortName in record.members:
- self._db_execute(
- """
- insert into GROUPS (SHORT_NAME, MEMBER_RECORD_TYPE, MEMBER_SHORT_NAME)
- values (:1, :2, :3)
- """, shortName, memberRecordType, memberShortName
- )
-
- # CUAddress
- for cuaddr in record.calendarUserAddresses:
- self._db_execute(
- """
- insert into ADDRESSES (ADDRESS, SHORT_NAME)
- values (:1, :2)
- """, cuaddr, shortName
- )
-
- def _delete_from_db(self, shortName):
- """
- Deletes the specified entry from all dbs.
- @param name: the name of the resource to delete.
- @param shortName: the short name of the resource to delete.
- """
- self._db_execute("delete from ACCOUNTS where SHORT_NAME = :1", shortName)
- self._db_execute("delete from GROUPS where SHORT_NAME = :1", shortName)
- self._db_execute("delete from GROUPS where MEMBER_SHORT_NAME = :1", shortName)
- self._db_execute("delete from ADDRESSES where SHORT_NAME = :1", shortName)
-
- def _db_version(self):
- """
- @return: the schema version assigned to this index.
- """
- return SQLDirectoryManager.dbFormatVersion
-
- def _db_type(self):
- """
- @return: the collection type assigned to this index.
- """
- return SQLDirectoryManager.dbType
-
- def _db_init_data_tables(self, q):
- """
- Initialise the underlying database tables.
- @param q: a database cursor to use.
- """
- #
- # SERVICE table
- #
- q.execute("create table SERVICE (REALM text)")
-
- #
- # ACCOUNTS table
- #
- q.execute(
- """
- create table ACCOUNTS (
- RECORD_TYPE text,
- SHORT_NAME text,
- GUID text,
- PASSWORD text,
- NAME text
- )
- """
- )
-
- #
- # GROUPS table
- #
- q.execute(
- """
- create table GROUPS (
- SHORT_NAME text,
- MEMBER_RECORD_TYPE text,
- MEMBER_SHORT_NAME text
- )
- """
- )
-
- #
- # ADDRESSES table
- #
- q.execute(
- """
- create table ADDRESSES (
- ADDRESS text unique,
- SHORT_NAME text
- )
- """
- )
-
-class SQLDirectoryService(DirectoryService):
- """
- XML based implementation of L{IDirectoryService}.
- """
- baseGUID = "8256E464-35E0-4DBB-A99C-F0E30C231675"
- realmName = None
-
- def __repr__(self):
- return "<%s %r: %r>" % (self.__class__.__name__, self.realmName, self.manager.dbpath)
-
- def __init__(self, dbParentPath, xmlFile=None):
- super(SQLDirectoryService, self).__init__()
-
- if type(dbParentPath) is str:
- dbParentPath = FilePath(dbParentPath)
-
- self.manager = SQLDirectoryManager(dbParentPath.path)
- if xmlFile:
- self.manager.loadFromXML(xmlFile)
- self.realmName = self.manager.getRealm()
-
- def recordTypes(self):
- recordTypes = (
- DirectoryService.recordType_users,
- DirectoryService.recordType_groups,
- DirectoryService.recordType_locations,
- DirectoryService.recordType_resources,
- )
- return recordTypes
-
- def listRecords(self, recordType):
- for result in self.manager.listRecords(recordType):
- yield SQLDirectoryRecord(
- service = self,
- recordType = recordType,
- shortName = result[0],
- guid = result[1],
- password = result[2],
- name = result[3],
- members = result[4],
- groups = result[5],
- calendarUserAddresses = result[6],
- autoSchedule = result[7],
- )
-
- def recordWithShortName(self, recordType, shortName):
- result = self.manager.getRecord(recordType, shortName)
- if result:
- return SQLDirectoryRecord(
- service = self,
- recordType = recordType,
- shortName = result[0],
- guid = result[1],
- password = result[2],
- name = result[3],
- members = result[4],
- groups = result[5],
- calendarUserAddresses = result[6],
- autoSchedule = result[7],
- )
-
- return None
-
-class SQLDirectoryRecord(DirectoryRecord):
- """
- XML based implementation implementation of L{IDirectoryRecord}.
- """
- def __init__(self, service, recordType, shortName, guid, password, name, members, groups, calendarUserAddresses, autoSchedule):
- super(SQLDirectoryRecord, self).__init__(
- service = service,
- recordType = recordType,
- guid = guid,
- shortName = shortName,
- fullName = name,
- calendarUserAddresses = calendarUserAddresses,
- autoSchedule = autoSchedule,
- )
-
- self.password = password
- self._members = members
- self._groups = groups
-
- def members(self):
- for recordType, shortName in self._members:
- yield self.service.recordWithShortName(recordType, shortName)
-
- def groups(self):
- for shortName in self._groups:
- yield self.service.recordWithShortName(DirectoryService.recordType_groups, shortName)
-
- def verifyCredentials(self, credentials):
- if isinstance(credentials, UsernamePassword):
- return credentials.password == self.password
-
- return super(SQLDirectoryRecord, self).verifyCredentials(credentials)
-
-if __name__ == '__main__':
- mgr = SQLDirectoryManager("./")
- mgr.loadFromXML("test/accounts.xml")
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/sudo.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/sudo.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/sudo.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -132,12 +132,13 @@
guid=None,
shortName=shortName,
fullName=shortName,
- calendarUserAddresses=set(),
- autoSchedule=False,
- enabledForCalendaring=False)
+ emailAddresses=set(),
+ )
self.password = entry['password']
+ self.enabled = True # Explicitly enabled
+
def verifyCredentials(self, credentials):
if IUsernamePassword.providedBy(credentials):
return credentials.checkPassword(self.password)
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/accounts.xml
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/accounts.xml 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/accounts.xml 2009-09-08 20:38:33 UTC (rev 4529)
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
-Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+Copyright (c) 2006-2009 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.
@@ -16,7 +16,7 @@
limitations under the License.
-->
-<!DOCTYPE accounts SYSTEM "../../../conf/accounts.dtd">
+<!DOCTYPE accounts SYSTEM "../../../conf/auth/accounts.dtd">
<accounts realm="Test">
<user>
@@ -30,36 +30,45 @@
<guid>6423F94A-6B76-4A3A-815B-D52CFD77935D</guid>
<password>zehcnasw</password>
<name>Wilfredo Sanchez</name>
- <cuaddr>mailto:wsanchez at example.com</cuaddr>
+ <email-address>wsanchez at example.com</email-address>
</user>
<user>
<uid>cdaboo</uid>
<guid>5A985493-EE2C-4665-94CF-4DFEA3A89500</guid>
<password>oobadc</password>
<name>Cyrus Daboo</name>
- <cuaddr>mailto:cdaboo at example.com</cuaddr>
+ <email-address>cdaboo at example.com</email-address>
</user>
<user>
<uid>lecroy</uid>
<guid>8B4288F6-CC82-491D-8EF9-642EF4F3E7D0</guid>
<password>yorcel</password>
<name>Chris Lecroy</name>
- <cuaddr>mailto:lecroy at example.com</cuaddr>
+ <email-address>lecroy at example.com</email-address>
</user>
<user>
<uid>dreid</uid>
<guid>5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</guid>
<password>dierd</password>
<name>David Reid</name>
- <cuaddr>mailto:dreid at example.com</cuaddr>
+ <email-address>dreid at example.com</email-address>
</user>
+ <user>
+ <uid>nocalendar</uid>
+ <guid>543D28BA-F74F-4D5F-9243-B3E3A61171E5</guid>
+ <password>radnelacon</password>
+ <name>No Calendar</name>
+ <email-address>nocalendar at example.com</email-address>
+ </user>
<user repeat="2">
<uid>user%02d</uid>
+ <guid>user%02d</guid>
<password>%02duser</password>
<name>User %02d</name>
</user>
<group>
<uid>managers</uid>
+ <guid>9FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</guid>
<password>managers</password>
<name>Managers</name>
<members>
@@ -68,6 +77,7 @@
</group>
<group>
<uid>admin</uid>
+ <guid>admin</guid>
<password>admin</password>
<name>Administrators</name>
<members>
@@ -76,6 +86,7 @@
</group>
<group>
<uid>grunts</uid>
+ <guid>grunts</guid>
<password>grunts</password>
<name>We do all the work</name>
<members>
@@ -86,6 +97,7 @@
</group>
<group>
<uid>right_coast</uid>
+ <guid>right_coast</guid>
<password>right_coast</password>
<name>East Coast</name>
<members>
@@ -94,6 +106,7 @@
</group>
<group>
<uid>left_coast</uid>
+ <guid>left_coast</guid>
<password>left_coast</password>
<name>West Coast</name>
<members>
@@ -104,6 +117,7 @@
</group>
<group>
<uid>both_coasts</uid>
+ <guid>both_coasts</guid>
<password>both_coasts</password>
<name>Both Coasts</name>
<members>
@@ -113,6 +127,7 @@
</group>
<group>
<uid>recursive1_coasts</uid>
+ <guid>recursive1_coasts</guid>
<password>recursive1_coasts</password>
<name>Recursive1 Coasts</name>
<members>
@@ -122,6 +137,7 @@
</group>
<group>
<uid>recursive2_coasts</uid>
+ <guid>recursive2_coasts</guid>
<password>recursive2_coasts</password>
<name>Recursive2 Coasts</name>
<members>
@@ -131,74 +147,61 @@
</group>
<group>
<uid>non_calendar_group</uid>
+ <guid>non_calendar_group</guid>
<password>non_calendar_group</password>
<name>Non-calendar group</name>
<members>
<member>cdaboo</member>
<member>lecroy</member>
</members>
- <disable-calendar/>
</group>
<location>
<uid>mercury</uid>
+ <guid>mercury</guid>
<password>mercury</password>
<name>Mecury Seven</name>
- <cuaddr>mailto:mercury at example.com</cuaddr>
- <proxies>
- <member type="groups">left_coast</member>
- </proxies>
+ <email-address>mercury at example.com</email-address>
</location>
<location>
<uid>gemini</uid>
+ <guid>gemini</guid>
<password>gemini</password>
<name>Gemini Twelve</name>
- <cuaddr>mailto:gemini at example.com</cuaddr>
- <auto-schedule/>
- <proxies>
- <member>wsanchez</member>
- </proxies>
+ <email-address>gemini at example.com</email-address>
</location>
<location>
<uid>apollo</uid>
+ <guid>apollo</guid>
<password>apollo</password>
<name>Apollo Eleven</name>
- <cuaddr>mailto:apollo at example.com</cuaddr>
- <proxies>
- <member type="groups">both_coasts</member>
- </proxies>
+ <email-address>apollo at example.com</email-address>
</location>
<location>
<uid>orion</uid>
+ <guid>orion</guid>
<password>orion</password>
<name>Orion</name>
- <cuaddr>mailto:orion at example.com</cuaddr>
- <proxies>
- <member type="groups">recursive1_coasts</member>
- </proxies>
+ <email-address>orion at example.com</email-address>
</location>
<resource>
<uid>transporter</uid>
+ <guid>transporter</guid>
<password>transporter</password>
<name>Mass Transporter</name>
- <cuaddr>mailto:transporter at example.com</cuaddr>
+ <email-address>transporter at example.com</email-address>
</resource>
<resource>
<uid>ftlcpu</uid>
+ <guid>ftlcpu</guid>
<password>ftlcpu</password>
<name>Faster-Than-Light Microprocessor</name>
- <cuaddr>mailto:ftlcpu at example.com</cuaddr>
+ <email-address>ftlcpu at example.com</email-address>
</resource>
<resource>
<uid>non_calendar_proxy</uid>
<guid>non_calendar_proxy</guid>
<password>non_calendar_proxy</password>
<name>Non-calendar proxy</name>
- <cuaddr>mailto:non_calendar_proxy at example.com</cuaddr>
- <proxies>
- <member type="groups">non_calendar_group</member>
- </proxies>
- <read-only-proxies>
- <member type="groups">recursive2_coasts</member>
- </read-only-proxies>
+ <email-address>non_calendar_proxy at example.com</email-address>
</resource>
</accounts>
Added: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/augments-test.xml
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/augments-test.xml (rev 0)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/augments-test.xml 2009-09-08 20:38:33 UTC (rev 4529)
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2009 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>D11F03A0-97EA-48AF-9A6C-FAC7F3975766</guid>
+ <enable>true</enable>
+ </record>
+ <record>
+ <guid>6423F94A-6B76-4A3A-815B-D52CFD77935D</guid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ <cuaddr>mailto:wsanchez at example.com</cuaddr>
+ </record>
+ <record>
+ <guid>5A985493-EE2C-4665-94CF-4DFEA3A89500</guid>
+ <enable>false</enable>
+ </record>
+ <record>
+ <guid>8B4288F6-CC82-491D-8EF9-642EF4F3E7D0</guid>
+ <enable>true</enable>
+ <enable-calendar>false</enable-calendar>
+ </record>
+ <record>
+ <guid>5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</guid>
+ <enable>true</enable>
+ <hosted-at>00001</hosted-at>
+ </record>
+ <record>
+ <guid>543D28BA-F74F-4D5F-9243-B3E3A61171E5</guid>
+ <enable>true</enable>
+ <hosted-at>00002</hosted-at>
+ </record>
+ <record>
+ <guid>6A73326A-F781-47E7-A9F8-AF47364D4152</guid>
+ <enable>true</enable>
+ <hosted-at>00002</hosted-at>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ <auto-schedule>true</auto-schedule>
+ <cuaddr>mailto:usera at example.com</cuaddr>
+ <cuaddr>mailto:user.a at example.com</cuaddr>
+ <cuaddr>mailto:user_a at example.com</cuaddr>
+ </record>
+</augments>
Added: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/augments.xml
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/augments.xml (rev 0)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/augments.xml 2009-09-08 20:38:33 UTC (rev 4529)
@@ -0,0 +1,129 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2006-2009 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 augments SYSTEM "../../../conf/augments.dtd">
+
+<augments realm="Test">
+ <record>
+ <guid>D11F03A0-97EA-48AF-9A6C-FAC7F3975766</guid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ </record>
+ <record>
+ <guid>6423F94A-6B76-4A3A-815B-D52CFD77935D</guid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ </record>
+ <record>
+ <guid>5A985493-EE2C-4665-94CF-4DFEA3A89500</guid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ </record>
+ <record>
+ <guid>8B4288F6-CC82-491D-8EF9-642EF4F3E7D0</guid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ </record>
+ <record>
+ <guid>5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</guid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ </record>
+ <record>
+ <guid>543D28BA-F74F-4D5F-9243-B3E3A61171E5</guid>
+ <enable>true</enable>
+ <enable-calendar>false</enable-calendar>
+ </record>
+ <record repeat="2">
+ <guid>user%02d</guid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ </record>
+ <record>
+ <guid>9FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</guid>
+ <enable>true</enable>
+ </record>
+ <record>
+ <guid>admin</guid>
+ <enable>true</enable>
+ </record>
+ <record>
+ <guid>grunts</guid>
+ <enable>true</enable>
+ </record>
+ <record>
+ <guid>right_coast</guid>
+ <enable>true</enable>
+ </record>
+ <record>
+ <guid>left_coast</guid>
+ <enable>true</enable>
+ </record>
+ <record>
+ <guid>both_coasts</guid>
+ <enable>true</enable>
+ </record>
+ <record>
+ <guid>recursive1_coasts</guid>
+ <enable>true</enable>
+ </record>
+ <record>
+ <guid>recursive2_coasts</guid>
+ <enable>true</enable>
+ </record>
+ <record>
+ <guid>non_calendar_group</guid>
+ <enable>true</enable>
+ </record>
+ <record>
+ <guid>mercury</guid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ </record>
+ <record>
+ <guid>gemini</guid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ <auto-schedule>true</auto-schedule>
+ </record>
+ <record>
+ <guid>apollo</guid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ </record>
+ <record>
+ <guid>orion</guid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ </record>
+ <record>
+ <guid>transporter</guid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ </record>
+ <record>
+ <guid>ftlcpu</guid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ </record>
+ <record>
+ <guid>non_calendar_proxy</guid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ </record>
+</augments>
Deleted: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/basic
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/basic 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/basic 2009-09-08 20:38:33 UTC (rev 4529)
@@ -1,4 +0,0 @@
-wsanchez:Cytm0Bwm7CPJs
-cdaboo:I.Ef5FJl5GVh2
-dreid:LVhqAv4qSrYPs
-lecroy:/7/5VDrkrLxY.
Deleted: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/digest
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/digest 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/digest 2009-09-08 20:38:33 UTC (rev 4529)
@@ -1,4 +0,0 @@
-wsanchez:Test:decbe233ab3d997cacc2fc058b19db8c
-cdaboo:Test:61164bf3d607d072fe8a7ac420b24aac
-dreid:Test:8ee67801004b2752f72b84e7064889a6
-lecroy:Test:60d4feb424430953be045738041e51be
Deleted: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/groups
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/groups 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/groups 2009-09-08 20:38:33 UTC (rev 4529)
@@ -1,4 +0,0 @@
-managers: lecroy
-grunts: wsanchez, cdaboo, dreid
-right_coast: cdaboo
-left_coast: wsanchez, dreid, lecroy
Added: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/proxies.xml
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/proxies.xml (rev 0)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/proxies.xml 2009-09-08 20:38:33 UTC (rev 4529)
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2006-2009 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 proxies SYSTEM "proxies.dtd">
+
+<proxies>
+ <record>
+ <guid>mercury</guid>
+ <proxies>
+ <member>left_coast</member>
+ </proxies>
+ </record>
+ <record>
+ <guid>gemini</guid>
+ <proxies>
+ <member>6423F94A-6B76-4A3A-815B-D52CFD77935D</member>
+ </proxies>
+ </record>
+ <record>
+ <guid>apollo</guid>
+ <proxies>
+ <member>both_coasts</member>
+ </proxies>
+ </record>
+ <record>
+ <guid>orion</guid>
+ <proxies>
+ <member>recursive1_coasts</member>
+ </proxies>
+ </record>
+ <record>
+ <guid>non_calendar_proxy</guid>
+ <proxies>
+ <member>non_calendar_group</member>
+ </proxies>
+ <read-only-proxies>
+ <member>recursive2_coasts</member>
+ </read-only-proxies>
+ </record>
+</proxies>
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_aggregate.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_aggregate.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_aggregate.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -1,5 +1,5 @@
##
-# Copyright (c) 2005-2007 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2009 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.
@@ -14,20 +14,18 @@
# limitations under the License.
##
-from twistedcaldav.directory.apache import BasicDirectoryService
from twistedcaldav.directory.xmlfile import XMLDirectoryService
from twistedcaldav.directory.aggregate import AggregateDirectoryService
-from twistedcaldav.directory.test.test_apache import digestRealm, basicUserFile, groupFile
-from twistedcaldav.directory.test.test_xmlfile import xmlFile
+from twistedcaldav.directory.test.test_xmlfile import xmlFile, augmentsFile
import twistedcaldav.directory.test.util
+from twistedcaldav.directory import augment
apache_prefix = "apache:"
xml_prefix = "xml:"
testServices = (
- (apache_prefix, twistedcaldav.directory.test.test_apache.Apache ),
(xml_prefix , twistedcaldav.directory.test.test_xmlfile.XMLFile),
)
@@ -65,10 +63,9 @@
"""
Returns an IDirectoryService.
"""
- apacheService = BasicDirectoryService(digestRealm, basicUserFile, groupFile)
- apacheService.recordTypePrefix = apache_prefix
-
- xmlService = XMLDirectoryService(xmlFile)
+ xmlService = XMLDirectoryService(xmlFile=xmlFile)
xmlService.recordTypePrefix = xml_prefix
- return AggregateDirectoryService((apacheService, xmlService))
+ augment.AugmentService = augment.AugmentXMLDB(xmlFiles=(augmentsFile.path,))
+
+ return AggregateDirectoryService((xmlService,))
Deleted: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_apache.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_apache.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_apache.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -1,120 +0,0 @@
-##
-# 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.
-##
-
-import os
-
-from twisted.python.filepath import FilePath
-
-import twistedcaldav.directory.test.util
-from twistedcaldav.directory.apache import BasicDirectoryService, DigestDirectoryService
-from twistedcaldav.directory.directory import DirectoryService
-
-digestRealm = "Test"
-
-basicUserFile = FilePath(os.path.join(os.path.dirname(__file__), "basic"))
-digestUserFile = FilePath(os.path.join(os.path.dirname(__file__), "digest"))
-groupFile = FilePath(os.path.join(os.path.dirname(__file__), "groups"))
-
-# FIXME: Add tests for GUID hooey, once we figure out what that means here
-
-class Apache (object):
- recordTypes = set((
- DirectoryService.recordType_users,
- DirectoryService.recordType_groups
- ))
-
- users = {
- "wsanchez": { "password": "foo", "guid": None, "addresses": () },
- "cdaboo" : { "password": "bar", "guid": None, "addresses": () },
- "dreid" : { "password": "baz", "guid": None, "addresses": () },
- "lecroy" : { "password": "quux", "guid": None, "addresses": () },
- }
-
- groups = {
- "managers" : { "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users, "lecroy"),) },
- "grunts" : { "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users, "wsanchez"),
- (DirectoryService.recordType_users, "cdaboo"),
- (DirectoryService.recordType_users, "dreid")) },
- "right_coast": { "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users, "cdaboo"),) },
- "left_coast" : { "guid": None, "addresses": (), "members": ((DirectoryService.recordType_users, "wsanchez"),
- (DirectoryService.recordType_users, "dreid"),
- (DirectoryService.recordType_users, "lecroy")) },
- }
-
- locations = {
- }
-
- resources = {
- }
-
- def service(self):
- return self.serviceClass(digestRealm, self.userFile(), self.groupFile())
-
- userFileName = None
-
- def userFile(self):
- if not hasattr(self, "_userFile"):
- if self.userFileName is None:
- raise NotImplementedError("Test subclass needs to specify userFileName.")
- self._userFile = FilePath(self.mktemp())
- basicUserFile.copyTo(self._userFile)
- return self._userFile
-
- def groupFile(self):
- if not hasattr(self, "_groupFile"):
- self._groupFile = FilePath(self.mktemp())
- groupFile.copyTo(self._groupFile)
- return self._groupFile
-
- def test_changedGroupFile(self):
- self.groupFile().open("w").write("grunts: wsanchez\n")
- self.assertEquals(self.recordNames(DirectoryService.recordType_groups), set(("grunts",)))
-
- def test_recordTypes_user(self):
- """
- IDirectoryService.recordTypes(userFile)
- """
- self.assertEquals(set(self.serviceClass(digestRealm, self.userFile()).recordTypes()), set((DirectoryService.recordType_users,)))
-
- userEntry = None
-
- def test_changedUserFile(self):
- if self.userEntry is None:
- raise NotImplementedError("Test subclass needs to specify userEntry.")
- self.userFile().open("w").write(self.userEntry[1])
- self.assertEquals(self.recordNames(DirectoryService.recordType_users), set((self.userEntry[0],)))
-
-class Basic (Apache, twistedcaldav.directory.test.util.BasicTestCase):
- """
- Test Apache-Compatible UserFile/GroupFile directory implementation.
- """
- serviceClass = BasicDirectoryService
-
- userFileName = basicUserFile
- userEntry = ("wsanchez", "wsanchez:Cytm0Bwm7CPJs\n")
-
-class Digest (Apache, twistedcaldav.directory.test.util.DigestTestCase):
- """
- Test Apache-Compatible DigestFile/GroupFile directory implementation.
- """
- serviceClass = DigestDirectoryService
-
- userFileName = digestUserFile
- userEntry = ("wsanchez", "wsanchez:Test:decbe233ab3d997cacc2fc058b19db8c\n")
-
- def test_verifyCredentials_digest(self):
- raise NotImplementedError() # Use super's implementation
- test_verifyCredentials_digest.todo = "unimplemented"
Added: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_augment.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_augment.py (rev 0)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_augment.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -0,0 +1,108 @@
+##
+# Copyright (c) 2009 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.
+##
+
+from twistedcaldav.test.util import TestCase
+from twistedcaldav.directory.augment import AugmentXMLDB, AugmentSqliteDB
+from twisted.internet.defer import inlineCallbacks
+from twistedcaldav.directory.xmlaugmentsparser import XMLAugmentsParser
+import cStringIO
+import os
+
+xmlFile = os.path.join(os.path.dirname(__file__), "augments-test.xml")
+
+testRecords = (
+ {"guid":"D11F03A0-97EA-48AF-9A6C-FAC7F3975766", "enabled":True, "hostedAt":"", "enabledForCalendaring":False, "autoSchedule":False, "calendarUserAddresses":set()},
+ {"guid":"6423F94A-6B76-4A3A-815B-D52CFD77935D", "enabled":True, "hostedAt":"", "enabledForCalendaring":True, "autoSchedule":False, "calendarUserAddresses":set(("mailto:wsanchez at example.com",))},
+ {"guid":"5A985493-EE2C-4665-94CF-4DFEA3A89500", "enabled":False, "hostedAt":"", "enabledForCalendaring":False, "autoSchedule":False, "calendarUserAddresses":set()},
+ {"guid":"8B4288F6-CC82-491D-8EF9-642EF4F3E7D0", "enabled":True, "hostedAt":"", "enabledForCalendaring":False, "autoSchedule":False, "calendarUserAddresses":set()},
+ {"guid":"5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1", "enabled":True, "hostedAt":"00001", "enabledForCalendaring":False, "autoSchedule":False, "calendarUserAddresses":set()},
+ {"guid":"543D28BA-F74F-4D5F-9243-B3E3A61171E5", "enabled":True, "hostedAt":"00002", "enabledForCalendaring":False, "autoSchedule":False, "calendarUserAddresses":set()},
+ {"guid":"6A73326A-F781-47E7-A9F8-AF47364D4152", "enabled":True, "hostedAt":"00002", "enabledForCalendaring":True, "autoSchedule":True, "calendarUserAddresses":set(("mailto:usera at example.com", "mailto:user.a at example.com", "mailto:user_a at example.com",))},
+)
+
+class AugmentTests(TestCase):
+
+ @inlineCallbacks
+ def _checkRecord(self, db, items):
+
+ record = (yield db.getAugmentRecord(items["guid"]))
+ self.assertTrue(record is not None)
+
+ for k,v in items.iteritems():
+ self.assertEqual(getattr(record, k), v)
+
+ @inlineCallbacks
+ def _checkNoRecord(self, db, guid):
+
+ record = (yield db.getAugmentRecord(guid))
+ self.assertTrue(record is None)
+
+class AugmentXMLTests(AugmentTests):
+
+ @inlineCallbacks
+ def test_read(self):
+
+ db = AugmentXMLDB((xmlFile,))
+
+ for item in testRecords:
+ yield self._checkRecord(db, item)
+
+ yield self._checkNoRecord(db, "D11F03A0-97EA-48AF-9A6C-FAC7F3975767")
+
+ def test_parseErrors(self):
+
+ db = {}
+ self.assertRaises(RuntimeError, XMLAugmentsParser, cStringIO.StringIO(""), db)
+ self.assertRaises(RuntimeError, XMLAugmentsParser, cStringIO.StringIO("""<?xml version="1.0" encoding="utf-8"?>
+<accounts>
+ <foo/>
+</accounts>
+"""), db)
+ self.assertRaises(RuntimeError, XMLAugmentsParser, cStringIO.StringIO("""<?xml version="1.0" encoding="utf-8"?>
+<augments>
+ <foo/>
+</augments>
+"""), db)
+ self.assertRaises(RuntimeError, XMLAugmentsParser, cStringIO.StringIO("""<?xml version="1.0" encoding="utf-8"?>
+<augments>
+ <record>
+ <enable>true</enable>
+ </record>
+</augments>
+"""), db)
+ self.assertRaises(RuntimeError, XMLAugmentsParser, cStringIO.StringIO("""<?xml version="1.0" encoding="utf-8"?>
+ <record>
+ <guid>admin</guid>
+ <enable>true</enable>
+ <foo/>
+ </record>
+"""), db)
+
+class AugmentSqliteTests(AugmentTests):
+
+ @inlineCallbacks
+ def test_read(self):
+
+ db = AugmentSqliteDB(self.mktemp())
+
+ dbxml = AugmentXMLDB((xmlFile,))
+ for record in dbxml.db.values():
+ yield db.addAugmentRecord(record)
+
+ for item in testRecords:
+ yield self._checkRecord(db, item)
+
+ yield self._checkNoRecord(db, "D11F03A0-97EA-48AF-9A6C-FAC7F3975767")
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_calendar.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_calendar.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_calendar.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -1,5 +1,5 @@
##
-# Copyright (c) 2005-2007 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2009 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.
@@ -16,9 +16,11 @@
import os
+from twisted.internet.defer import inlineCallbacks
from twisted.web2.dav import davxml
from twisted.web2.test.test_server import SimpleRequest
+from twistedcaldav import caldavxml
from twistedcaldav.directory.principal import DirectoryPrincipalProvisioningResource
from twistedcaldav.directory.test.test_xmlfile import xmlFile
from twistedcaldav.directory.xmlfile import XMLDirectoryService
@@ -34,11 +36,11 @@
super(ProvisionedCalendars, self).setUp()
# Setup the initial directory
- self.xmlfile = self.mktemp()
- fd = open(self.xmlfile, "w")
+ self.xmlFile = self.mktemp()
+ fd = open(self.xmlFile, "w")
fd.write(open(xmlFile.path, "r").read())
fd.close()
- self.directoryService = XMLDirectoryService(self.xmlfile)
+ self.directoryService = XMLDirectoryService(xmlFile=self.xmlFile)
# Set up a principals hierarchy for each service we're testing with
name = "principals"
@@ -69,3 +71,33 @@
request = SimpleRequest(self.site, "GET", "/calendars/users/12345/")
d = request.locateResource(request.uri)
d.addCallback(_response)
+
+ def test_ExistentCalendarHome(self):
+
+ def _response(resource):
+ if resource is None:
+ self.fail("Incorrect response to GET on existent calendar home.")
+
+ request = SimpleRequest(self.site, "GET", "/calendars/users/wsanchez/")
+ d = request.locateResource(request.uri)
+ d.addCallback(_response)
+
+ def test_ExistentCalendar(self):
+
+ def _response(resource):
+ if resource is None:
+ self.fail("Incorrect response to GET on existent calendar.")
+
+ request = SimpleRequest(self.site, "GET", "/calendars/users/wsanchez/calendar/")
+ d = request.locateResource(request.uri)
+ d.addCallback(_response)
+
+ def test_ExistentInbox(self):
+
+ def _response(resource):
+ if resource is None:
+ self.fail("Incorrect response to GET on existent inbox.")
+
+ request = SimpleRequest(self.site, "GET", "/calendars/users/wsanchez/inbox/")
+ d = request.locateResource(request.uri)
+ d.addCallback(_response)
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_digest.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_digest.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_digest.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -2,12 +2,12 @@
from twisted.cred import error
from twisted.internet import address
-from twisted.trial import unittest
from twisted.web2.auth import digest
from twisted.web2.auth.wrapper import UnauthorizedResponse
from twisted.web2.test.test_server import SimpleRequest
from twisted.web2.dav.fileop import rmdir
from twistedcaldav.directory.digest import QopDigestCredentialFactory
+from twistedcaldav.test.util import TestCase
import os
import md5
@@ -71,7 +71,7 @@
emtpyAttributeAuthRequest = 'realm="",nonce="doesn\'t matter"'
-class DigestAuthTestCase(unittest.TestCase):
+class DigestAuthTestCase(TestCase):
"""
Test the behavior of DigestCredentialFactory
"""
@@ -80,6 +80,7 @@
"""
Create a DigestCredentialFactory for testing
"""
+ TestCase.setUp(self)
self.path1 = self.mktemp()
self.path2 = self.mktemp()
os.mkdir(self.path1)
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_guidchange.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_guidchange.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_guidchange.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -1,5 +1,5 @@
##
-# Copyright (c) 2005-2007 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2009 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.
@@ -37,11 +37,11 @@
super(ProvisionedPrincipals, self).setUp()
# Setup the initial directory
- self.xmlfile = self.mktemp()
- fd = open(self.xmlfile, "w")
+ self.xmlFile = self.mktemp()
+ fd = open(self.xmlFile, "w")
fd.write(open(xmlFile.path, "r").read())
fd.close()
- self.directoryService = XMLDirectoryService(self.xmlfile)
+ self.directoryService = XMLDirectoryService(xmlFile=self.xmlFile)
# Set up a principals hierarchy for each service we're testing with
name = "principals"
@@ -78,7 +78,7 @@
def privs1(result):
# Change GUID in record
- fd = open(self.xmlfile, "w")
+ fd = open(self.xmlFile, "w")
fd.write(open(xmlFile.path, "r").read().replace(oldUID, newUID))
fd.close()
fd = None
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_opendirectory.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_opendirectory.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_opendirectory.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -1,5 +1,5 @@
##
-# Copyright (c) 2005-2007 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2009 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.
@@ -21,8 +21,10 @@
else:
import twisted.web2.auth.digest
import twistedcaldav.directory.test.util
+ from twistedcaldav.directory import augment
from twistedcaldav.directory.directory import DirectoryService
from twistedcaldav.directory.appleopendirectory import OpenDirectoryRecord
+ import dsattributes
# Wonky hack to prevent unclean reactor shutdowns
class DummyReactor(object):
@@ -51,6 +53,7 @@
def setUp(self):
super(OpenDirectory, self).setUp()
self._service = OpenDirectoryService(node="/Search", dosetup=False)
+ augment.AugmentService = augment.AugmentXMLDB(xmlFiles=())
def tearDown(self):
for call in self._service._delayedCalls:
@@ -67,12 +70,8 @@
nodeName = "/LDAPv2/127.0.0.1",
shortName = "user",
fullName = "Some user",
- calendarUserAddresses = set(("mailtoguid at example.com",)),
- autoSchedule = False,
- enabledForCalendaring = True,
+ emailAddresses = set(("someuser at example.com",)),
memberGUIDs = [],
- proxyGUIDs = (),
- readOnlyProxyGUIDs = (),
)
digestFields = {}
Deleted: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_opendirectoryrecords.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_opendirectoryrecords.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_opendirectoryrecords.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -1,483 +0,0 @@
-##
-# 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.
-##
-
-import twisted.trial.unittest
-
-try:
- from twistedcaldav.directory.appleopendirectory import OpenDirectoryService as RealOpenDirectoryService
- import dsattributes
-except ImportError:
- pass
-else:
- from twistedcaldav.directory.directory import DirectoryService
- from twistedcaldav.directory.util import uuidFromName
-
- class OpenDirectoryService (RealOpenDirectoryService):
- def _queryDirectory(self, recordType, shortName=None, guid=None):
- if shortName is None and guid is None:
- return self.fakerecords[recordType]
-
- assert shortName is None or guid is None
- if guid is not None:
- guid = guid.lower()
-
- records = []
-
- for name, record in self.fakerecords[recordType]:
- if name == shortName or record[dsattributes.kDS1AttrGeneratedUID] == guid:
- records.append((name, record))
-
- return tuple(records)
-
- class ReloadCache(twisted.trial.unittest.TestCase):
- def setUp(self):
- super(ReloadCache, self).setUp()
- self._service = OpenDirectoryService(node="/Search", dosetup=False)
- self._service.servicetags.add("FE588D50-0514-4DF9-BCB5-8ECA5F3DA274:030572AE-ABEC-4E0F-83C9-FCA304769E5F:calendar")
-
- def tearDown(self):
- for call in self._service._delayedCalls:
- call.cancel()
-
- def _verifyRecords(self, recordType, expected):
- expected = set(expected)
- found = set(self._service._records[recordType]["records"].keys())
-
- missing = expected.difference(found)
- extras = found.difference(expected)
-
- self.assertTrue(len(missing) == 0, msg="Directory records not found: %s" % (missing,))
- self.assertTrue(len(extras) == 0, msg="Directory records not expected: %s" % (extras,))
-
- def _verifyRecordsCheckEnabled(self, recordType, expected, enabled):
- expected = set(expected)
- found = set([item for item in self._service._records[recordType]["records"].iterkeys()
- if self._service._records[recordType]["records"][item].enabledForCalendaring == enabled])
-
- missing = expected.difference(found)
- extras = found.difference(expected)
-
- self.assertTrue(len(missing) == 0, msg="Directory records not found: %s" % (missing,))
- self.assertTrue(len(extras) == 0, msg="Directory records not expected: %s" % (extras,))
-
- def _verifyDisabledRecords(self, recordType, expectedNames, expectedGUIDs):
- def check(disabledType, expected):
- expected = set(expected)
- found = self._service._records[recordType][disabledType]
-
- missing = expected.difference(found)
- extras = found.difference(expected)
-
- self.assertTrue(len(missing) == 0, msg="Disabled directory records not found: %s" % (missing,))
- self.assertTrue(len(extras) == 0, msg="Disabled directory records not expected: %s" % (extras,))
-
- check("disabled names", expectedNames)
- check("disabled guids", (guid.lower() for guid in expectedGUIDs))
-
- def test_normal(self):
- self._service.fakerecords = {
- DirectoryService.recordType_users: [
- fakeODRecord("User 01"),
- fakeODRecord("User 02"),
- ],
- DirectoryService.recordType_groups: [
- fakeODRecord("Group 01"),
- fakeODRecord("Group 02"),
- ],
- DirectoryService.recordType_resources: [
- fakeODRecord("Resource 01"),
- fakeODRecord("Resource 02"),
- ],
- DirectoryService.recordType_locations: [
- fakeODRecord("Location 01"),
- fakeODRecord("Location 02"),
- ],
- }
-
- self._service.reloadCache(DirectoryService.recordType_users)
- self._service.reloadCache(DirectoryService.recordType_groups)
- self._service.reloadCache(DirectoryService.recordType_resources)
- self._service.reloadCache(DirectoryService.recordType_locations)
-
- self._verifyRecords(DirectoryService.recordType_users, ("user01", "user02"))
- self._verifyDisabledRecords(DirectoryService.recordType_users, (), ())
-
- self._verifyRecords(DirectoryService.recordType_groups, ("group01", "group02"))
- self._verifyDisabledRecords(DirectoryService.recordType_groups, (), ())
-
- self._verifyRecords(DirectoryService.recordType_resources, ("resource01", "resource02"))
- self._verifyDisabledRecords(DirectoryService.recordType_resources, (), ())
-
- self._verifyRecords(DirectoryService.recordType_locations, ("location01", "location02"))
- self._verifyDisabledRecords(DirectoryService.recordType_locations, (), ())
-
- def test_normal_disabledusers(self):
- self._service.fakerecords = {
- DirectoryService.recordType_users: [
- fakeODRecord("User 01"),
- fakeODRecord("User 02"),
- fakeODRecord("User 03", addLocator=False),
- fakeODRecord("User 04", addLocator=False),
- ],
- DirectoryService.recordType_groups: [
- fakeODRecord("Group 01"),
- fakeODRecord("Group 02"),
- fakeODRecord("Group 03", addLocator=False),
- fakeODRecord("Group 04", addLocator=False),
- ],
- DirectoryService.recordType_resources: [
- fakeODRecord("Resource 01"),
- fakeODRecord("Resource 02"),
- fakeODRecord("Resource 03", addLocator=False),
- fakeODRecord("Resource 04", addLocator=False),
- ],
- DirectoryService.recordType_locations: [
- fakeODRecord("Location 01"),
- fakeODRecord("Location 02"),
- fakeODRecord("Location 03", addLocator=False),
- fakeODRecord("Location 04", addLocator=False),
- ],
- }
-
- self._service.reloadCache(DirectoryService.recordType_users)
- self._service.reloadCache(DirectoryService.recordType_groups)
- self._service.reloadCache(DirectoryService.recordType_resources)
- self._service.reloadCache(DirectoryService.recordType_locations)
-
- self._verifyRecordsCheckEnabled(DirectoryService.recordType_users, ("user01", "user02"), True)
- self._verifyRecordsCheckEnabled(DirectoryService.recordType_users, ("user03", "user04"), False)
-
- self._verifyRecordsCheckEnabled(DirectoryService.recordType_groups, ("group01", "group02"), True)
- self._verifyRecordsCheckEnabled(DirectoryService.recordType_groups, ("group03", "group04"), False)
-
- self._verifyRecordsCheckEnabled(DirectoryService.recordType_resources, ("resource01", "resource02"), True)
- self._verifyRecordsCheckEnabled(DirectoryService.recordType_resources, (), False)
-
- self._verifyRecordsCheckEnabled(DirectoryService.recordType_locations, ("location01", "location02"), True)
- self._verifyRecordsCheckEnabled(DirectoryService.recordType_locations, (), False)
-
- def test_normalCacheMiss(self):
- self._service.fakerecords = {
- DirectoryService.recordType_users: [
- fakeODRecord("User 01"),
- ],
- }
-
- self._service.reloadCache(DirectoryService.recordType_users)
-
- self._verifyRecords(DirectoryService.recordType_users, ("user01",))
- self._verifyDisabledRecords(DirectoryService.recordType_users, (), ())
-
- self._service.fakerecords = {
- DirectoryService.recordType_users: [
- fakeODRecord("User 01"),
- fakeODRecord("User 02"),
- fakeODRecord("User 03", guid="D10F3EE0-5014-41D3-8488-3819D3EF3B2A"),
- ],
- }
-
- self._service.reloadCache(DirectoryService.recordType_users, shortName="user02")
- self._service.reloadCache(DirectoryService.recordType_users, guid="D10F3EE0-5014-41D3-8488-3819D3EF3B2A")
-
- self._verifyRecords(DirectoryService.recordType_users, ("user01", "user02", "user03"))
- self._verifyDisabledRecords(DirectoryService.recordType_users, (), ())
-
- def test_duplicateRecords(self):
- self._service.fakerecords = {
- DirectoryService.recordType_users: [
- fakeODRecord("User 01"),
- fakeODRecord("User 02"),
- fakeODRecord("User 02"),
- ],
- }
-
- self._service.reloadCache(DirectoryService.recordType_users)
-
- self._verifyRecords(DirectoryService.recordType_users, ("user01", "user02"))
- self._verifyDisabledRecords(DirectoryService.recordType_users, (), ())
- self._verifyDisabledRecords(DirectoryService.recordType_users, (), ())
-
-
- def test_duplicateName(self):
- self._service.fakerecords = {
- DirectoryService.recordType_users: [
- fakeODRecord("User 01"),
- fakeODRecord("User 02", guid="A25775BB-1281-4606-98C6-2893B2D5CCD7"),
- fakeODRecord("User 02", guid="30CA2BB9-C935-4A5D-80E2-79266BCB0255"),
- ],
- }
-
- self._service.reloadCache(DirectoryService.recordType_users)
-
- self._verifyRecords(DirectoryService.recordType_users, ("user01",))
- self._verifyDisabledRecords(
- DirectoryService.recordType_users,
- ("user02",),
- ("A25775BB-1281-4606-98C6-2893B2D5CCD7", "30CA2BB9-C935-4A5D-80E2-79266BCB0255"),
- )
-
- def test_duplicateGUID(self):
- self._service.fakerecords = {
- DirectoryService.recordType_users: [
- fakeODRecord("User 01"),
- fakeODRecord("User 02", guid="113D7F74-F84A-4F17-8C96-CE8F10D68EF8"),
- fakeODRecord("User 03", guid="113D7F74-F84A-4F17-8C96-CE8F10D68EF8"),
- ],
- }
-
- self._service.reloadCache(DirectoryService.recordType_users)
-
- self._verifyRecords(DirectoryService.recordType_users, ("user01",))
- self._verifyDisabledRecords(
- DirectoryService.recordType_users,
- ("user02", "user03"),
- ("113D7F74-F84A-4F17-8C96-CE8F10D68EF8",),
- )
-
- def test_duplicateCombo(self):
- self._service.fakerecords = {
- DirectoryService.recordType_users: [
- fakeODRecord("User 01"),
- fakeODRecord("User 02", guid="113D7F74-F84A-4F17-8C96-CE8F10D68EF8"),
- fakeODRecord("User 02", guid="113D7F74-F84A-4F17-8C96-CE8F10D68EF8", shortName="user03"),
- fakeODRecord("User 02", guid="136E369F-DB40-4135-878D-B75D38242D39"),
- ],
- }
-
- self._service.reloadCache(DirectoryService.recordType_users)
-
- self._verifyRecords(DirectoryService.recordType_users, ("user01",))
- self._verifyDisabledRecords(
- DirectoryService.recordType_users,
- ("user02", "user03"),
- ("113D7F74-F84A-4F17-8C96-CE8F10D68EF8", "136E369F-DB40-4135-878D-B75D38242D39"),
- )
-
- def test_duplicateGUIDCacheMiss(self):
- self._service.fakerecords = {
- DirectoryService.recordType_users: [
- fakeODRecord("User 01"),
- fakeODRecord("User 02", guid="EDB9EE55-31F2-4EA9-B5FB-D8AE2A8BA35E"),
- fakeODRecord("User 03", guid="D10F3EE0-5014-41D3-8488-3819D3EF3B2A"),
- ],
- }
-
- self._service.reloadCache(DirectoryService.recordType_users)
-
- self._verifyRecords(DirectoryService.recordType_users, ("user01", "user02", "user03"))
- self._verifyDisabledRecords(DirectoryService.recordType_users, (), ())
-
- self._service.fakerecords = {
- DirectoryService.recordType_users: [
- fakeODRecord("User 01"),
- fakeODRecord("User 02", guid="EDB9EE55-31F2-4EA9-B5FB-D8AE2A8BA35E"),
- fakeODRecord("User 02", guid="EDB9EE55-31F2-4EA9-B5FB-D8AE2A8BA35E", shortName="user04"),
- fakeODRecord("User 03", guid="62368DDF-0C62-4C97-9A58-DE9FD46131A0"),
- fakeODRecord("User 03", guid="62368DDF-0C62-4C97-9A58-DE9FD46131A0", shortName="user05"),
- ],
- }
-
- self._service.reloadCache(DirectoryService.recordType_users, shortName="user04")
- self._service.reloadCache(DirectoryService.recordType_users, guid="62368DDF-0C62-4C97-9A58-DE9FD46131A0")
-
- self._verifyRecords(DirectoryService.recordType_users, ("user01",))
- self._verifyDisabledRecords(
- DirectoryService.recordType_users,
- ("user02", "user03", "user04", "user05"),
- ("EDB9EE55-31F2-4EA9-B5FB-D8AE2A8BA35E", "62368DDF-0C62-4C97-9A58-DE9FD46131A0", "D10F3EE0-5014-41D3-8488-3819D3EF3B2A"),
- )
-
- def test_groupmembers(self):
- self._service.fakerecords = {
- DirectoryService.recordType_users: [
- fakeODRecord("User 01"),
- fakeODRecord("User 02"),
- ],
- DirectoryService.recordType_groups: [
- fakeODRecord("Group 01", members=[
- guidForShortName("user01"),
- guidForShortName("user02"),
- ]),
- fakeODRecord("Group 02", members=[
- guidForShortName("resource01"),
- guidForShortName("user02"),
- ]),
- ],
- DirectoryService.recordType_resources: [
- fakeODRecord("Resource 01"),
- fakeODRecord("Resource 02"),
- ],
- DirectoryService.recordType_locations: [
- fakeODRecord("Location 01"),
- fakeODRecord("Location 02"),
- ],
- }
-
- self._service.reloadCache(DirectoryService.recordType_users)
- self._service.reloadCache(DirectoryService.recordType_groups)
- self._service.reloadCache(DirectoryService.recordType_resources)
- self._service.reloadCache(DirectoryService.recordType_locations)
-
- group1 = self._service.recordWithShortName(DirectoryService.recordType_groups, "group01")
- self.assertTrue(group1 is not None)
-
- group2 = self._service.recordWithShortName(DirectoryService.recordType_groups, "group02")
- self.assertTrue(group2 is not None)
-
- user1 = self._service.recordWithShortName(DirectoryService.recordType_users, "user01")
- self.assertTrue(user1 is not None)
- self.assertEqual(set((group1,)), user1.groups())
-
- user2 = self._service.recordWithShortName(DirectoryService.recordType_users, "user02")
- self.assertTrue(user2 is not None)
- self.assertEqual(set((group1, group2)), user2.groups())
-
- self._service.fakerecords[DirectoryService.recordType_groups] = [
- fakeODRecord("Group 01", members=[
- guidForShortName("user01"),
- ]),
- fakeODRecord("Group 02", members=[
- guidForShortName("resource01"),
- guidForShortName("user02"),
- ]),
- ]
- self._service.reloadCache(DirectoryService.recordType_groups)
-
- group1 = self._service.recordWithShortName(DirectoryService.recordType_groups, "group01")
- self.assertTrue(group1 is not None)
-
- group2 = self._service.recordWithShortName(DirectoryService.recordType_groups, "group02")
- self.assertTrue(group2 is not None)
-
- user1 = self._service.recordWithShortName(DirectoryService.recordType_users, "user01")
- self.assertTrue(user1 is not None)
- self.assertEqual(set((group1,)), user1.groups())
-
- user2 = self._service.recordWithShortName(DirectoryService.recordType_users, "user02")
- self.assertTrue(user2 is not None)
- self.assertEqual(set((group2,)), user2.groups())
-
- self._service.fakerecords[DirectoryService.recordType_groups] = [
- fakeODRecord("Group 03", members=[
- guidForShortName("user01"),
- guidForShortName("user02"),
- ]),
- ]
- self._service.reloadCache(DirectoryService.recordType_groups, guid=guidForShortName("group03"))
-
- group1 = self._service.recordWithShortName(DirectoryService.recordType_groups, "group01")
- self.assertTrue(group1 is not None)
-
- group2 = self._service.recordWithShortName(DirectoryService.recordType_groups, "group02")
- self.assertTrue(group2 is not None)
-
- group3 = self._service.recordWithShortName(DirectoryService.recordType_groups, "group03")
- self.assertTrue(group2 is not None)
-
- user1 = self._service.recordWithShortName(DirectoryService.recordType_users, "user01")
- self.assertTrue(user1 is not None)
- self.assertEqual(set((group1, group3)), user1.groups())
-
- user2 = self._service.recordWithShortName(DirectoryService.recordType_users, "user02")
- self.assertTrue(user2 is not None)
- self.assertEqual(set((group2, group3)), user2.groups())
-
- def test_calendaruseraddress(self):
- self._service.fakerecords = {
- DirectoryService.recordType_users: [
- fakeODRecord("User 01"),
- fakeODRecord("User 02"),
- ],
- DirectoryService.recordType_groups: [],
- DirectoryService.recordType_resources: [],
- DirectoryService.recordType_locations: [],
- }
-
- self._service.reloadCache(DirectoryService.recordType_users)
-
- user1 = self._service.recordWithCalendarUserAddress("mailto:user01 at example.com")
- self.assertTrue(user1 is not None)
-
- user3 = self._service.recordWithCalendarUserAddress("mailto:user03 at example.com")
- self.assertTrue(user3 is None)
-
- self._service.fakerecords = {
- DirectoryService.recordType_users: [
- fakeODRecord("User 01"),
- fakeODRecord("User 02"),
- fakeODRecord("User 03"),
- ],
- DirectoryService.recordType_groups: [],
- DirectoryService.recordType_resources: [],
- DirectoryService.recordType_locations: [],
- }
- self._service.reloadCache(DirectoryService.recordType_users)
-
- user1 = self._service.recordWithCalendarUserAddress("mailto:user01 at example.com")
- self.assertTrue(user1 is not None)
-
- user3 = self._service.recordWithCalendarUserAddress("mailto:user03 at example.com")
- self.assertTrue(user3 is not None)
-
- self._service.fakerecords = {
- DirectoryService.recordType_users: [
- fakeODRecord("User 02"),
- fakeODRecord("User 03"),
- ],
- DirectoryService.recordType_groups: [],
- DirectoryService.recordType_resources: [],
- DirectoryService.recordType_locations: [],
- }
- self._service.reloadCache(DirectoryService.recordType_users)
-
- user1 = self._service.recordWithCalendarUserAddress("mailto:user01 at example.com")
- self.assertTrue(user1 is None)
-
- user3 = self._service.recordWithCalendarUserAddress("mailto:user03 at example.com")
- self.assertTrue(user3 is not None)
-
-def fakeODRecord(fullName, shortName=None, guid=None, email=None, addLocator=True, members=None):
- if shortName is None:
- shortName = shortNameForFullName(fullName)
-
- if guid is None:
- guid = guidForShortName(shortName)
- else:
- guid = guid.lower()
-
- if email is None:
- email = "%s at example.com" % (shortName,)
-
- attrs = {
- dsattributes.kDS1AttrDistinguishedName: fullName,
- dsattributes.kDS1AttrGeneratedUID: guid,
- dsattributes.kDSNAttrEMailAddress: email,
- dsattributes.kDSNAttrMetaNodeLocation: "/LDAPv3/127.0.0.1",
- }
-
- if members:
- attrs[dsattributes.kDSNAttrGroupMembers] = members
-
- if addLocator:
- attrs[dsattributes.kDSNAttrServicesLocator] = "FE588D50-0514-4DF9-BCB5-8ECA5F3DA274:030572AE-ABEC-4E0F-83C9-FCA304769E5F:calendar"
-
- return [ shortName, attrs ]
-
-def shortNameForFullName(fullName):
- return fullName.lower().replace(" ", "")
-
-def guidForShortName(shortName):
- return uuidFromName(OpenDirectoryService.baseGUID, shortName)
Deleted: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_opendirectoryschema.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_opendirectoryschema.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_opendirectoryschema.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -1,1298 +0,0 @@
-##
-# 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.
-##
-
-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>access</key>
- <dict>
- <key>somethingorother</key>
- <string>somethingelse</string>
- </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>access</key>
- <dict>
- <key>somethingorother</key>
- <string>somethingelse</string>
- </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>access</key>
- <dict>
- <key>somethingorother</key>
- <string>somethingelse</string>
- </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>access</key>
- <dict>
- <key>somethingorother</key>
- <string>somethingelse</string>
- </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>access</key>
- <dict>
- <key>somethingorother</key>
- <string>somethingelse</string>
- </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>access</key>
- <dict>
- <key>somethingorother</key>
- <string>somethingelse</string>
- </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>access</key>
- <dict>
- <key>somethingorother</key>
- <string>somethingelse</string>
- </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>access</key>
- <dict>
- <key>somethingorother</key>
- <string>somethingelse</string>
- </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_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>access</key>
- <dict>
- <key>somethingorother</key>
- <string>somethingelse</string>
- </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>access</key>
- <dict>
- <key>somethingorother</key>
- <string>somethingelse</string>
- </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>access</key>
- <dict>
- <key>somethingorother</key>
- <string>somethingelse</string>
- </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>access</key>
- <dict>
- <key>somethingorother</key>
- <string>somethingelse</string>
- </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_duplicate = """<?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>F4088107-51FD-4DE5-904D-C20AD9C6C893</key>
- <dict>
- <key>hostname</key>
- <string>foo.apple.com</string>
-
- <key>hostDetails</key>
- <dict>
- <key>access</key>
- <dict>
- <key>somethingorother</key>
- <string>somethingelse</string>
- </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>1C8C34AC-3D9E-403C-8A33-FBC303F3840E</key>
- <dict>
- <key>hostname</key>
- <string>calendar.apple.com</string>
-
- <key>hostDetails</key>
- <dict>
- <key>access</key>
- <dict>
- <key>somethingorother</key>
- <string>somethingelse</string>
- </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._parseServiceInfo("calendar.apple.com", "recordit", {
- 'dsAttrTypeNative:apple-serviceinfo' : plist,
- dsattributes.kDS1AttrGeneratedUID: "GUIDIFY",
- dsattributes.kDSNAttrMetaNodeLocation: "/LDAPv3/127.0.0.1"}) and service.servicetags:
- 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"),
- )
- for plist, title in plists:
- _doParse(plist, title)
-
- def test_goodplist(self):
- service = OpenDirectoryService(node="/Search", dosetup=False)
- if not service._parseServiceInfo("calendar.apple.com", "recordit", {
- 'dsAttrTypeNative:apple-serviceinfo' : PlistParse.plist_good,
- dsattributes.kDS1AttrGeneratedUID: "GUIDIFY",
- dsattributes.kDSNAttrMetaNodeLocation: "/LDAPv3/127.0.0.1"}):
- self.fail(msg="Plist parse should not have failed")
- else:
- # Verify that we extracted the proper items
- self.assertEqual(service.servicetags.pop(), "GUIDIFY:C18C34AC-3D9E-403C-8A33-BFC303F3840E:calendar")
-
- def test_expandcuaddrs(self):
- def doTest(recordName, record, result, title):
- service = OpenDirectoryService(node="/Search", dosetup=False)
- if not service._parseServiceInfo("calendar.apple.com", recordName, {
- 'dsAttrTypeNative:apple-serviceinfo' : PlistParse.plist_good,
- dsattributes.kDS1AttrGeneratedUID: "GUIDIFY",
- dsattributes.kDSNAttrMetaNodeLocation: "/LDAPv3/127.0.0.1"}):
- self.fail(msg="Plist parse should not have failed: %s" % (recordName,))
- else:
- expanded = service._calendarUserAddresses(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((
- "mailto:user01 at example.com",
- )),
- "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((
- "mailto:user02 at example.com",
- "mailto:user02 at calendar.example.com",
- )),
- "User with multiple email addresses, %s != %s",
- ),
- (
- "user03",
- {
- dsattributes.kDS1AttrGeneratedUID: "GUID-USER-03",
- },
- set(()),
- "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_localod_good = ("computer1.apple.com", {
- dsattributes.kDS1AttrGeneratedUID : "GUID1",
- dsattributes.kDSNAttrRecordName : "computer1.apple.com",
- 'dsAttrTypeNative:apple-serviceinfo' : PlistParse.plist_good,
- dsattributes.kDSNAttrMetaNodeLocation : "/LDAPv3/127.0.0.1",
- })
- record_localod_good_other = ("computer2.apple.com", {
- dsattributes.kDS1AttrGeneratedUID : "GUID1",
- dsattributes.kDSNAttrRecordName : "computer2.apple.com",
- 'dsAttrTypeNative:apple-serviceinfo' : PlistParse.plist_good_other,
- dsattributes.kDSNAttrMetaNodeLocation : "/LDAPv3/127.0.0.1",
- })
- record_localod_duplicate = ("computer1", {
- dsattributes.kDS1AttrGeneratedUID : "GUID1_bad",
- dsattributes.kDSNAttrRecordName : "computer1",
- 'dsAttrTypeNative:apple-serviceinfo' : PlistParse.plist_duplicate,
- dsattributes.kDSNAttrMetaNodeLocation : "/LDAPv3/127.0.0.1",
- })
- record_remoteod_good = ("computer3.apple.com", {
- dsattributes.kDS1AttrGeneratedUID : "GUID2",
- dsattributes.kDSNAttrRecordName : "computer3.apple.com",
- 'dsAttrTypeNative:apple-serviceinfo' : PlistParse.plist_good,
- dsattributes.kDSNAttrMetaNodeLocation : "/LDAPv3/directory.apple.com",
- })
- record_remoteod_duplicate = ("computer3", {
- dsattributes.kDS1AttrGeneratedUID : "GUID2",
- dsattributes.kDSNAttrRecordName : "computer3",
- 'dsAttrTypeNative:apple-serviceinfo' : PlistParse.plist_duplicate,
- dsattributes.kDSNAttrMetaNodeLocation : "/LDAPv3/directory.apple.com",
- })
- record_default_good = ("computer4.apple.com", {
- dsattributes.kDS1AttrGeneratedUID : "GUID3",
- dsattributes.kDSNAttrRecordName : "computer4.apple.com",
- 'dsAttrTypeNative:apple-serviceinfo' : PlistParse.plist_good,
- dsattributes.kDSNAttrMetaNodeLocation : "/Local/Default",
- })
- record_default_duplicate = ("computer4", {
- dsattributes.kDS1AttrGeneratedUID : "GUID3",
- dsattributes.kDSNAttrRecordName : "computer4",
- 'dsAttrTypeNative:apple-serviceinfo' : PlistParse.plist_duplicate,
- dsattributes.kDSNAttrMetaNodeLocation : "/Local/Default",
- })
-
- 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_localod_good_other[0], ODRecordsParse.record_localod_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_localod_good[0], ODRecordsParse.record_localod_good[1]),
- ), "single good plist"),
- ((
- (ODRecordsParse.record_localod_good[0], ODRecordsParse.record_localod_good[1]),
- (ODRecordsParse.record_localod_good_other[0], ODRecordsParse.record_localod_good_other[1]),
- ), "multiple plists"),
- )
-
- for recordlist, title in records:
- _doParseRecords(recordlist, title)
-
- def test_odrecords_multiple(self):
- def _doParseRecords(recordlist, title, tags):
- service = OpenDirectoryService(node="/Search", dosetup=False)
- service._parseComputersRecords(recordlist, "calendar.apple.com")
-
- self.assertEquals(service.servicetags, set(tags),
- "Got wrong service tags: %s and %s" % (service.servicetags, set(tags),))
-
- records = (
- (((ODRecordsParse.record_remoteod_good[0], ODRecordsParse.record_remoteod_good[1]),
- (ODRecordsParse.record_localod_good[0], ODRecordsParse.record_localod_good[1]),
- (ODRecordsParse.record_default_good[0], ODRecordsParse.record_default_good[1])),
- "Three records",
- ("GUID2:C18C34AC-3D9E-403C-8A33-BFC303F3840E:calendar",
- "GUID1:C18C34AC-3D9E-403C-8A33-BFC303F3840E:calendar",
- "GUID3:C18C34AC-3D9E-403C-8A33-BFC303F3840E:calendar")),
- (((ODRecordsParse.record_localod_good[0], ODRecordsParse.record_localod_good[1]),
- (ODRecordsParse.record_default_good[0], ODRecordsParse.record_default_good[1])),
- "Two records",
- ("GUID1:C18C34AC-3D9E-403C-8A33-BFC303F3840E:calendar",
- "GUID3:C18C34AC-3D9E-403C-8A33-BFC303F3840E:calendar")),
- (((ODRecordsParse.record_default_good[0], ODRecordsParse.record_default_good[1]),),
- "One record",
- ("GUID3:C18C34AC-3D9E-403C-8A33-BFC303F3840E:calendar",)),
- )
-
- for recordlist, title, tags in records:
- _doParseRecords(recordlist, title, tags)
-
- def test_odrecords_duplicates(self):
- def _doParseRecords(recordlist, title, items, tags):
- service = OpenDirectoryService(node="/Search", dosetup=False)
- service._parseComputersRecords(recordlist, "calendar.apple.com")
- self.assertEquals(service.servicetags, set(tags))
-
- records = (
- (((ODRecordsParse.record_remoteod_good[0], ODRecordsParse.record_remoteod_good[1]),
- (ODRecordsParse.record_remoteod_duplicate[0], ODRecordsParse.record_remoteod_duplicate[1]),
- (ODRecordsParse.record_localod_good[0], ODRecordsParse.record_localod_good[1]),
- (ODRecordsParse.record_default_good[0], ODRecordsParse.record_default_good[1])),
- "Remote Record Duplicated", ("computer3.apple.com", "computer3",),
- ("GUID2:C18C34AC-3D9E-403C-8A33-BFC303F3840E:calendar",
- "GUID2:1C8C34AC-3D9E-403C-8A33-FBC303F3840E:calendar",
- "GUID1:C18C34AC-3D9E-403C-8A33-BFC303F3840E:calendar",
- "GUID3:C18C34AC-3D9E-403C-8A33-BFC303F3840E:calendar")),
- (((ODRecordsParse.record_localod_good[0], ODRecordsParse.record_localod_good[1]),
- (ODRecordsParse.record_localod_duplicate[0], ODRecordsParse.record_localod_duplicate[1]),
- (ODRecordsParse.record_default_good[0], ODRecordsParse.record_default_good[1])),
- "Local OD Duplicated", ("computer1.apple.com", "computer1",),
- ("GUID1:C18C34AC-3D9E-403C-8A33-BFC303F3840E:calendar",
- "GUID1_bad:1C8C34AC-3D9E-403C-8A33-FBC303F3840E:calendar",
- "GUID3:C18C34AC-3D9E-403C-8A33-BFC303F3840E:calendar")),
- (((ODRecordsParse.record_default_good[0], ODRecordsParse.record_default_good[1]),
- (ODRecordsParse.record_default_duplicate[0], ODRecordsParse.record_default_duplicate[1])),
- "Local Node Duplicated", ("computer4.apple.com", "computer4",),
- ("GUID3:C18C34AC-3D9E-403C-8A33-BFC303F3840E:calendar",
- "GUID3:1C8C34AC-3D9E-403C-8A33-FBC303F3840E:calendar")),
- )
-
- for recordlist, title, items, tags in records:
- _doParseRecords(recordlist, title, items, tags)
-
- class ODResourceInfoParse (twisted.trial.unittest.TestCase):
-
- plist_good_false = """<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>com.apple.WhitePagesFramework</key>
- <dict>
- <key>AutoAcceptsInvitation</key>
- <false/>
- <key>Label</key>
- <string>Location</string>
- <key>CalendaringDelegate</key>
- <string>1234-GUID-5678</string>
- <key>ReadOnlyCalendaringDelegate</key>
- <string>1234-GUID-5679</string>
- </dict>
-</dict>
-</plist>
-"""
-
- plist_good_true = """<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>com.apple.WhitePagesFramework</key>
- <dict>
- <key>AutoAcceptsInvitation</key>
- <true/>
- <key>Label</key>
- <string>Location</string>
- <key>CalendaringDelegate</key>
- <string></string>
- <key>ReadOnlyCalendaringDelegate</key>
- <string></string>
- </dict>
-</dict>
-</plist>
-"""
-
- plist_good_missing = """<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>com.apple.WhitePagesFramework</key>
- <dict>
- <key>Label</key>
- <string>Location</string>
- </dict>
-</dict>
-</plist>
-"""
-
- plist_bad = """<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>com.apple.WhitePagesFramework</key>
- <string>bogus</string>
-</dict>
-</plist>
-"""
-
- plist_wrong = """<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>com.apple.YellowPagesFramework</key>
- <dict>
- <key>AutoAcceptsInvitation</key>
- <true/>
- <key>Label</key>
- <string>Location</string>
- <key>CalendaringDelegate</key>
- <string>1234-GUID-5678</string>
- <key>ReadOnlyCalendaringDelegate</key>
- <string>1234-GUID-5679</string>
- </dict>
-</dict>
-</plist>
-"""
-
- plist_invalid = """<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>com.apple.WhitePagesFramework</key>
- <string>bogus</string>
- <string>another bogon</string>
-</dict>
-</plist>
-"""
-
- plist_invalid_xml = """<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>com.apple.WhitePagesFramework</key>
- <string>R&D</string>
-</dict>
-</plist>
-"""
-
- test_bool = (
- (plist_good_false, False, "1234-GUID-5678", "1234-GUID-5679", None),
- (plist_good_true, True, "", "", None),
- (plist_good_missing, False, None, None, None),
- (plist_wrong, False, None, None, None),
- (plist_bad, False, None, None, ValueError),
- (plist_invalid, False, None, None, ValueError),
- (plist_invalid_xml, False, None, None, ValueError),
- )
-
- def test_plists(self):
- service = OpenDirectoryService(node="/Search", dosetup=False)
-
- for item in ODResourceInfoParse.test_bool:
- if item[4] is None:
- item1, item2, item3 = service._parseResourceInfo(item[0], "guid", "locations", "name")
- self.assertEqual(item1, item[1])
- self.assertEqual(item2, item[2])
- self.assertEqual(item3, item[3])
- else:
- self.assertRaises(item[4], service._parseResourceInfo, item[0], "guid", "locations", "name")
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_principal.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_principal.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_principal.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -1,5 +1,5 @@
##
-# Copyright (c) 2005-2007 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2009 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.
@@ -16,19 +16,19 @@
import os
+from twisted.cred.credentials import UsernamePassword
from twisted.internet.defer import inlineCallbacks
from twisted.web2.dav import davxml
from twisted.web2.dav.fileop import rmdir
from twisted.web2.dav.resource import AccessDeniedError
from twisted.web2.test.test_server import SimpleRequest
-from twisted.web2.dav.test.util import serialize
from twistedcaldav.static import CalendarHomeProvisioningFile
-from twistedcaldav.directory.apache import BasicDirectoryService, DigestDirectoryService
+from twistedcaldav.config import config
+from twistedcaldav.directory import augment
from twistedcaldav.directory.directory import DirectoryService
-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.test.test_xmlfile import xmlFile, augmentsFile
from twistedcaldav.directory.principal import DirectoryPrincipalProvisioningResource
from twistedcaldav.directory.principal import DirectoryPrincipalTypeProvisioningResource
from twistedcaldav.directory.principal import DirectoryPrincipalResource
@@ -38,11 +38,6 @@
import twistedcaldav.test.util
-directoryServices = (
- BasicDirectoryService(digestRealm, basicUserFile, groupFile),
- DigestDirectoryService(digestRealm, digestUserFile, groupFile),
- XMLDirectoryService(xmlFile),
-)
class ProvisionedPrincipals (twistedcaldav.test.util.TestCase):
"""
@@ -51,9 +46,13 @@
def setUp(self):
super(ProvisionedPrincipals, self).setUp()
+ self.directoryServices = (
+ XMLDirectoryService(xmlFile=xmlFile),
+ )
+
# Set up a principals hierarchy for each service we're testing with
self.principalRootResources = {}
- for directory in directoryServices:
+ for directory in self.directoryServices:
name = directory.__class__.__name__
url = "/" + name + "/"
@@ -63,6 +62,8 @@
self.principalRootResources[directory.__class__.__name__] = provisioningResource
+ augment.AugmentService = augment.AugmentXMLDB(xmlFiles=(augmentsFile.path,))
+
def test_hierarchy(self):
"""
DirectoryPrincipalProvisioningResource.listChildren(),
@@ -77,7 +78,7 @@
DirectoryPrincipalResource.principalURL(),
"""
- for directory in directoryServices:
+ for directory in self.directoryServices:
#print "\n -> %s" % (directory.__class__.__name__,)
provisioningResource = self.principalRootResources[directory.__class__.__name__]
@@ -139,7 +140,7 @@
"""
DirectoryPrincipalProvisioningResource.principalForUser()
"""
- for directory in directoryServices:
+ for directory in self.directoryServices:
provisioningResource = self.principalRootResources[directory.__class__.__name__]
for user in directory.listRecords(DirectoryService.recordType_users):
@@ -162,7 +163,7 @@
"""
for provisioningResource, recordType, recordResource, record in self._allRecords():
principal = provisioningResource.principalForRecord(record)
- self.failIf(principal is None)
+ self.failIf(principal is None, msg=str(record))
self.assertEquals(record, principal.record)
def test_principalForCalendarUserAddress(self):
@@ -184,6 +185,13 @@
else:
self.failIf(principal is not None)
+ # Explicitly check the disabled record
+ provisioningResource = self.principalRootResources['XMLDirectoryService']
+ self.failIf(provisioningResource.principalForCalendarUserAddress("mailto:nocalendar at example.com") is not None)
+ self.failIf(provisioningResource.principalForCalendarUserAddress("urn:uuid:543D28BA-F74F-4D5F-9243-B3E3A61171E5") is not None)
+ self.failIf(provisioningResource.principalForCalendarUserAddress("/principals/users/nocalendar/") is not None)
+ self.failIf(provisioningResource.principalForCalendarUserAddress("/principals/__uids__/543D28BA-F74F-4D5F-9243-B3E3A61171E5/") is not None)
+
def test_autoSchedule(self):
"""
DirectoryPrincipalProvisioningResource.principalForCalendarUserAddress()
@@ -251,24 +259,6 @@
memberships = yield recordResource.groupMemberships()
self.failUnless(set(record.groups()).issubset(set(r.record for r in memberships if hasattr(r, "record"))))
- def test_proxies(self):
- """
- DirectoryPrincipalResource.proxies()
- """
- for provisioningResource, recordType, recordResource, record in self._allRecords():
- if record.enabledForCalendaring:
- self.failUnless(set(record.proxies()).issubset(set(r.record for r in recordResource.proxies())))
- self.assertEqual(record.hasEditableProxyMembership(), recordResource.hasEditableProxyMembership())
-
- def test_read_only_proxies(self):
- """
- DirectoryPrincipalResource.proxies()
- """
- for provisioningResource, recordType, recordResource, record in self._allRecords():
- if record.enabledForCalendaring:
- self.failUnless(set(record.readOnlyProxies()).issubset(set(r.record for r in recordResource.readOnlyProxies())))
- self.assertEqual(record.hasEditableProxyMembership(), recordResource.hasEditableProxyMembership())
-
def test_principalUID(self):
"""
DirectoryPrincipalResource.principalUID()
@@ -305,7 +295,7 @@
# Need to create a calendar home provisioner for each service.
calendarRootResources = {}
- for directory in directoryServices:
+ for directory in self.directoryServices:
url = "/homes_" + directory.__class__.__name__ + "/"
path = os.path.join(self.docroot, url[1:])
@@ -352,37 +342,29 @@
"""
Default access controls for principals.
"""
- def work():
- for provisioningResource, recordType, recordResource, record in self._allRecords():
- for args in _authReadOnlyPrivileges(recordResource, recordResource.principalURL()):
- yield args
+ for provisioningResource, recordType, recordResource, record in self._allRecords():
+ for args in _authReadOnlyPrivileges(self, recordResource, recordResource.principalURL()):
+ yield self._checkPrivileges(*args)
- for args in work():
- yield self._checkPrivileges(*args)
-
@inlineCallbacks
def test_defaultAccessControlList_provisioners(self):
"""
Default access controls for principal provisioning resources.
"""
- def work():
- for directory in directoryServices:
- #print "\n -> %s" % (directory.__class__.__name__,)
- provisioningResource = self.principalRootResources[directory.__class__.__name__]
+ for directory in self.directoryServices:
+ #print "\n -> %s" % (directory.__class__.__name__,)
+ provisioningResource = self.principalRootResources[directory.__class__.__name__]
- for args in _authReadOnlyPrivileges(provisioningResource, provisioningResource.principalCollectionURL()):
- yield args
+ for args in _authReadOnlyPrivileges(self, provisioningResource, provisioningResource.principalCollectionURL()):
+ yield self._checkPrivileges(*args)
- for recordType in provisioningResource.listChildren():
- #print " -> %s" % (recordType,)
- typeResource = provisioningResource.getChild(recordType)
+ for recordType in provisioningResource.listChildren():
+ #print " -> %s" % (recordType,)
+ typeResource = provisioningResource.getChild(recordType)
- for args in _authReadOnlyPrivileges(typeResource, typeResource.principalCollectionURL()):
- yield args
+ for args in _authReadOnlyPrivileges(self, typeResource, typeResource.principalCollectionURL()):
+ yield self._checkPrivileges(*args)
- for args in work():
- yield self._checkPrivileges(*args)
-
def _allRecords(self):
"""
@return: an iterable of tuples
@@ -393,7 +375,7 @@
C{record} is the directory service record
for each record in each directory in C{directoryServices}.
"""
- for directory in directoryServices:
+ for directory in self.directoryServices:
provisioningResource = self.principalRootResources[directory.__class__.__name__]
for recordType in directory.recordTypes():
for record in directory.listRecords(recordType):
@@ -425,13 +407,17 @@
d.addCallback(gotResource)
return d
-def _authReadOnlyPrivileges(resource, url):
- for principal, privilege, allowed in (
- ( davxml.All() , davxml.Read() , False ),
- ( davxml.All() , davxml.Write() , False ),
- ( davxml.Unauthenticated() , davxml.Read() , False ),
- ( davxml.Unauthenticated() , davxml.Write() , False ),
- ( davxml.Authenticated() , davxml.Read() , True ),
- ( davxml.Authenticated() , davxml.Write() , False ),
- ):
+def _authReadOnlyPrivileges(self, resource, url):
+ items = []
+ for provisioningResource, recordType, recordResource, record in self._allRecords():
+ if recordResource == resource:
+ items.append(( davxml.HRef().fromString(recordResource.principalURL()), davxml.Read() , True ))
+ items.append(( davxml.HRef().fromString(recordResource.principalURL()), davxml.Write() , True ))
+ else:
+ items.append(( davxml.HRef().fromString(recordResource.principalURL()), davxml.Read() , True ))
+ items.append(( davxml.HRef().fromString(recordResource.principalURL()), davxml.Write() , False ))
+ items.append(( davxml.Unauthenticated() , davxml.Read() , False ))
+ items.append(( davxml.Unauthenticated() , davxml.Write() , False ))
+
+ for principal, privilege, allowed in items:
yield resource, url, principal, privilege, allowed
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_proxyprincipalmembers.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_proxyprincipalmembers.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_proxyprincipalmembers.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -1,5 +1,5 @@
##
-# Copyright (c) 2005-2007 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2009 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.
@@ -14,39 +14,53 @@
# limitations under the License.
##
-from twisted.internet.defer import DeferredList, inlineCallbacks
+from twisted.internet.defer import DeferredList, inlineCallbacks, returnValue,\
+ succeed
from twisted.web2.dav import davxml
from twistedcaldav.directory.directory import DirectoryService
-from twistedcaldav.directory.xmlfile import XMLDirectoryService
-from twistedcaldav.directory.test.test_xmlfile import xmlFile
+from twistedcaldav.directory.test.test_xmlfile import xmlFile, augmentsFile,\
+ proxiesFile
from twistedcaldav.directory.principal import DirectoryPrincipalProvisioningResource
from twistedcaldav.directory.principal import DirectoryPrincipalResource
+from twistedcaldav.directory.xmlaccountsparser import XMLAccountsParser
+from twistedcaldav.directory.xmlfile import XMLDirectoryService
import twistedcaldav.test.util
+from twistedcaldav.config import config
+from twistedcaldav.directory import augment
+from twistedcaldav.directory.calendaruserproxyloader import XMLCalendarUserProxyLoader
+import os
-directoryService = XMLDirectoryService(xmlFile)
-
class ProxyPrincipals (twistedcaldav.test.util.TestCase):
"""
Directory service provisioned principals.
"""
+
+ @inlineCallbacks
def setUp(self):
super(ProxyPrincipals, self).setUp()
+ self.directoryService = XMLDirectoryService(xmlFile=xmlFile)
+ augment.AugmentService = augment.AugmentXMLDB(xmlFiles=(augmentsFile.path,))
+
# Set up a principals hierarchy for each service we're testing with
self.principalRootResources = {}
- name = directoryService.__class__.__name__
+ name = self.directoryService.__class__.__name__
url = "/" + name + "/"
- provisioningResource = DirectoryPrincipalProvisioningResource(url, directoryService)
+ provisioningResource = DirectoryPrincipalProvisioningResource(url, self.directoryService)
self.site.resource.putChild(name, provisioningResource)
- self.principalRootResources[directoryService.__class__.__name__] = provisioningResource
+ self.principalRootResources[self.directoryService.__class__.__name__] = provisioningResource
+ config.DataRoot = self.mktemp()
+ os.mkdir(config.DataRoot)
+ yield XMLCalendarUserProxyLoader(proxiesFile.path).updateProxyDB()
+
def _getPrincipalByShortName(self, type, name):
- provisioningResource = self.principalRootResources[directoryService.__class__.__name__]
+ provisioningResource = self.principalRootResources[self.directoryService.__class__.__name__]
return provisioningResource.principalForShortName(type, name)
def _groupMembersTest(self, recordType, recordName, subPrincipalName, expectedMembers):
@@ -76,6 +90,43 @@
return d
@inlineCallbacks
+ def _addProxy(self, principal, subPrincipalName, proxyPrincipal):
+
+ if isinstance(principal, tuple):
+ principal = self._getPrincipalByShortName(principal[0], principal[1])
+ principal = principal.getChild(subPrincipalName)
+ members = (yield principal.groupMembers())
+
+ if isinstance(proxyPrincipal, tuple):
+ proxyPrincipal = self._getPrincipalByShortName(proxyPrincipal[0], proxyPrincipal[1])
+ members.add(proxyPrincipal)
+
+ yield principal.setGroupMemberSetPrincipals(members)
+
+ @inlineCallbacks
+ def _removeProxy(self, recordType, recordName, subPrincipalName, proxyRecordType, proxyRecordName):
+
+ principal = self._getPrincipalByShortName(recordType, recordName)
+ principal = principal.getChild(subPrincipalName)
+ members = (yield principal.groupMembers())
+
+ proxyPrincipal = self._getPrincipalByShortName(proxyRecordType, proxyRecordName)
+ for p in members:
+ if p.principalUID() == proxyPrincipal.principalUID():
+ members.remove(p)
+ break
+
+ yield principal.setGroupMemberSetPrincipals(members)
+
+ @inlineCallbacks
+ def _clearProxy(self, principal, subPrincipalName):
+
+ if isinstance(principal, tuple):
+ principal = self._getPrincipalByShortName(principal[0], principal[1])
+ principal = principal.getChild(subPrincipalName)
+ yield principal.setGroupMemberSetPrincipals(set())
+
+ @inlineCallbacks
def _proxyForTest(self, recordType, recordName, expectedProxies, read_write):
principal = self._getPrincipalByShortName(recordType, recordName)
proxies = (yield principal.proxyFor(read_write))
@@ -201,6 +252,7 @@
d.addCallback(check)
return d
+ @inlineCallbacks
def test_setGroupMemberSet(self):
class StubMemberDB(object):
def __init__(self):
@@ -213,7 +265,7 @@
return self.members
- user = self._getPrincipalByShortName(directoryService.recordType_users,
+ user = self._getPrincipalByShortName(self.directoryService.recordType_users,
"cdaboo")
proxyGroup = user.getChild("calendar-proxy-write")
@@ -228,21 +280,22 @@
davxml.HRef.fromString(
"/XMLDirectoryService/__uids__/5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1/"))
- proxyGroup.setGroupMemberSet(new_members, None)
+ yield proxyGroup.setGroupMemberSet(new_members, None)
self.assertEquals(
set([str(p) for p in memberdb.members]),
set(["5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1",
"8B4288F6-CC82-491D-8EF9-642EF4F3E7D0"]))
-
+ @inlineCallbacks
def test_setGroupMemberSetNotifiesPrincipalCaches(self):
class StubCacheNotifier(object):
changedCount = 0
def changed(self):
self.changedCount += 1
+ return succeed(True)
- user = self._getPrincipalByShortName(directoryService.recordType_users, "cdaboo")
+ user = self._getPrincipalByShortName(self.directoryService.recordType_users, "cdaboo")
proxyGroup = user.getChild("calendar-proxy-write")
@@ -255,7 +308,7 @@
self.assertEquals(notifier.changedCount, 0)
- proxyGroup.setGroupMemberSet(
+ yield proxyGroup.setGroupMemberSet(
davxml.GroupMemberSet(
davxml.HRef.fromString(
"/XMLDirectoryService/__uids__/5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1/")),
@@ -281,3 +334,186 @@
False
)
+ @inlineCallbacks
+ def test_UserProxy(self):
+
+ for proxyType in ("calendar-proxy-read", "calendar-proxy-write"):
+
+ yield self._addProxy(
+ (DirectoryService.recordType_users, "wsanchez",),
+ proxyType,
+ (DirectoryService.recordType_users, "cdaboo",),
+ )
+
+ yield self._groupMembersTest(
+ DirectoryService.recordType_users, "wsanchez",
+ proxyType,
+ ("Cyrus Daboo",),
+ )
+
+ yield self._addProxy(
+ (DirectoryService.recordType_users, "wsanchez",),
+ proxyType,
+ (DirectoryService.recordType_users, "lecroy",),
+ )
+
+ yield self._groupMembersTest(
+ DirectoryService.recordType_users, "wsanchez",
+ proxyType,
+ ("Cyrus Daboo", "Chris Lecroy",),
+ )
+
+ yield self._removeProxy(
+ DirectoryService.recordType_users, "wsanchez",
+ proxyType,
+ DirectoryService.recordType_users, "cdaboo",
+ )
+
+ yield self._groupMembersTest(
+ DirectoryService.recordType_users, "wsanchez",
+ proxyType,
+ ("Chris Lecroy",),
+ )
+
+ @inlineCallbacks
+ def test_InvalidUserProxy(self):
+
+
+ # Set up the in-memory (non-null) memcacher:
+ config.ProcessType = "Single"
+ principal = self._getPrincipalByShortName(
+ DirectoryService.recordType_users, "wsanchez")
+ db = principal._calendar_user_proxy_index()
+
+ # Set the clock to the epoch:
+ theTime = 0
+ db._memcacher.theTime = theTime
+
+
+ for doMembershipFirst in (True, False):
+ for proxyType in ("calendar-proxy-read", "calendar-proxy-write"):
+
+ principal = self._getPrincipalByShortName(DirectoryService.recordType_users, "wsanchez")
+ proxyGroup = principal.getChild(proxyType)
+
+ testPrincipal = self._getPrincipalByShortName(DirectoryService.recordType_users, "cdaboo")
+
+ fakePrincipal = self._getPrincipalByShortName(DirectoryService.recordType_users, "dreid")
+ fakeProxyGroup = fakePrincipal.getChild(proxyType)
+
+ yield self._addProxy(
+ principal,
+ proxyType,
+ testPrincipal,
+ )
+
+ members = yield proxyGroup._index().getMembers(proxyGroup.uid)
+ self.assertEquals(len(members), 1)
+
+ yield self._addProxy(
+ fakePrincipal,
+ proxyType,
+ testPrincipal,
+ )
+ members = yield fakeProxyGroup._index().getMembers(fakeProxyGroup.uid)
+ self.assertEquals(len(members), 1)
+
+ uids = [p.principalUID() for p in (yield testPrincipal.groupMemberships())]
+ self.assertTrue("5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1#%s" % (proxyType,) in uids)
+
+ memberships = yield testPrincipal._calendar_user_proxy_index().getMemberships(testPrincipal.principalUID())
+ self.assertEquals(len(memberships), 2)
+
+ yield self._addProxy(
+ principal,
+ proxyType,
+ fakePrincipal,
+ )
+ members = yield proxyGroup._index().getMembers(proxyGroup.uid)
+ self.assertEquals(len(members), 2)
+
+ # Remove the dreid user from the directory service
+ del self.directoryService._accounts()[DirectoryService.recordType_users]["dreid"]
+
+
+ cacheTimeout = config.DirectoryService.params.get("cacheTimeout", 30) * 60 * 2
+
+ @inlineCallbacks
+ def _membershipTest():
+
+ uids = [p.principalUID() for p in (yield testPrincipal.groupMemberships())]
+ self.assertTrue("5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1#%s" % (proxyType,) not in uids)
+
+ memberships = yield testPrincipal._calendar_user_proxy_index().getMemberships(testPrincipal.principalUID())
+ self.assertEquals(len(memberships), 1)
+
+ @inlineCallbacks
+ def _membersTest(theTime):
+ yield self._groupMembersTest(
+ DirectoryService.recordType_users, "wsanchez",
+ proxyType,
+ ("Cyrus Daboo",),
+ )
+
+ # Trigger the proxy DB clean up, which won't actually
+ # remove anything because we haven't exceeded the timeout
+ yield proxyGroup.groupMembers()
+
+ # Advance 10 seconds
+ theTime += 10
+ db._memcacher.theTime = theTime
+
+ # When we first examine the members, we have not exceeded
+ # the clean-up timeout, so we'll still have 2:
+ members = yield proxyGroup._index().getMembers(proxyGroup.uid)
+ self.assertEquals(len(members), 2)
+
+ # Restore removed user
+ parser = XMLAccountsParser(self.directoryService.xmlFile)
+ self.directoryService._parsedAccounts = parser.items
+ self.directoryService.recordWithShortName(
+ DirectoryService.recordType_users, "dreid")
+
+ # Trigger the proxy DB clean up, which will actually
+ # remove the deletion timer because the principal has been
+ # restored
+ yield proxyGroup.groupMembers()
+
+ # Verify the deletion timer has been removed
+ result = yield db._memcacher.checkDeletionTimer("5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1")
+ self.assertEquals(result, None)
+
+ # Remove the dreid user from the directory service
+ del self.directoryService._accounts()[DirectoryService.recordType_users]["dreid"]
+
+ # Trigger the proxy DB clean up, which won't actually
+ # remove anything because we haven't exceeded the timeout
+ yield proxyGroup.groupMembers()
+
+ # Advance beyond the timeout
+ theTime += cacheTimeout
+ db._memcacher.theTime = theTime
+
+ # Trigger the proxy DB clean up
+ yield proxyGroup.groupMembers()
+
+ # The missing principal has now been cleaned out of the
+ # proxy DB
+ members = yield proxyGroup._index().getMembers(proxyGroup.uid)
+ self.assertEquals(len(members), 1)
+ returnValue(theTime)
+
+
+ if doMembershipFirst:
+ yield _membershipTest()
+ theTime = yield _membersTest(theTime)
+ else:
+ theTime = yield _membersTest(theTime)
+ yield _membershipTest()
+
+ # Restore removed user
+ parser = XMLAccountsParser(self.directoryService.xmlFile)
+ self.directoryService._parsedAccounts = parser.items
+
+ yield self._clearProxy(principal, proxyType)
+ yield self._clearProxy(fakePrincipal, proxyType)
Deleted: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_sqldb.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_sqldb.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_sqldb.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -1,51 +0,0 @@
-##
-# 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.
-##
-
-import os
-
-from twisted.python.filepath import FilePath
-
-import twistedcaldav.directory.test.util
-import twistedcaldav.directory.test.test_xmlfile
-from twistedcaldav.directory.sqldb import SQLDirectoryService
-
-xmlFile = FilePath(os.path.join(os.path.dirname(__file__), "accounts.xml"))
-
-# FIXME: Add tests for GUID hooey, once we figure out what that means here
-
-class SQLDB (
- twistedcaldav.directory.test.test_xmlfile.XMLFileBase,
- twistedcaldav.directory.test.util.BasicTestCase,
- twistedcaldav.directory.test.util.DigestTestCase
-):
- """
- Test SQL directory implementation.
- """
- def service(self):
- return SQLDirectoryService(os.getcwd(), self.xmlFile())
-
- def test_verifyCredentials_digest(self):
- super(SQLDB, self).test_verifyCredentials_digest()
- test_verifyCredentials_digest.todo = ""
-
- def test_verifyRealmFromDB(self):
- # Make sure the database has been initialized with the XML file
- self.service()
-
- # Then get an instance without using the XML file
- service = SQLDirectoryService(os.getcwd(), None)
-
- self.assertEquals(service.realmName, "Test")
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_util.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_util.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_util.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -15,12 +15,11 @@
##
from twistedcaldav.directory.util import uuidFromName
+from twistedcaldav.test.util import TestCase
-import twisted.trial.unittest
-
uuid_namespace_dns = "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
-class UUID (twisted.trial.unittest.TestCase):
+class UUID (TestCase):
def test_uuidFromName(self):
self.assertEquals(
uuidFromName(uuid_namespace_dns, "python.org"),
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_xmlfile.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_xmlfile.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/test_xmlfile.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -1,5 +1,5 @@
##
-# Copyright (c) 2005-2007 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2009 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.
@@ -18,11 +18,14 @@
from twisted.python.filepath import FilePath
+from twistedcaldav.directory import augment
from twistedcaldav.directory.directory import DirectoryService
import twistedcaldav.directory.test.util
from twistedcaldav.directory.xmlfile import XMLDirectoryService
xmlFile = FilePath(os.path.join(os.path.dirname(__file__), "accounts.xml"))
+augmentsFile = FilePath(os.path.join(os.path.dirname(__file__), "augments.xml"))
+proxiesFile = FilePath(os.path.join(os.path.dirname(__file__), "proxies.xml"))
# FIXME: Add tests for GUID hooey, once we figure out what that means here
@@ -35,13 +38,14 @@
))
users = {
- "admin" : { "password": "nimda", "guid": "D11F03A0-97EA-48AF-9A6C-FAC7F3975766", "addresses": () },
- "wsanchez": { "password": "zehcnasw", "guid": "6423F94A-6B76-4A3A-815B-D52CFD77935D", "addresses": ("mailto:wsanchez at example.com",) },
- "cdaboo" : { "password": "oobadc", "guid": "5A985493-EE2C-4665-94CF-4DFEA3A89500", "addresses": ("mailto:cdaboo at example.com",) },
- "lecroy" : { "password": "yorcel", "guid": "8B4288F6-CC82-491D-8EF9-642EF4F3E7D0", "addresses": ("mailto:lecroy at example.com",) },
- "dreid" : { "password": "dierd", "guid": "5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1", "addresses": ("mailto:dreid at example.com",) },
- "user01" : { "password": "01user", "guid": None , "addresses": () },
- "user02" : { "password": "02user", "guid": None , "addresses": () },
+ "admin" : { "password": "nimda", "guid": "D11F03A0-97EA-48AF-9A6C-FAC7F3975766", "addresses": () },
+ "wsanchez" : { "password": "zehcnasw", "guid": "6423F94A-6B76-4A3A-815B-D52CFD77935D", "addresses": ("mailto:wsanchez at example.com",) },
+ "cdaboo" : { "password": "oobadc", "guid": "5A985493-EE2C-4665-94CF-4DFEA3A89500", "addresses": ("mailto:cdaboo at example.com",) },
+ "lecroy" : { "password": "yorcel", "guid": "8B4288F6-CC82-491D-8EF9-642EF4F3E7D0", "addresses": ("mailto:lecroy at example.com",) },
+ "dreid" : { "password": "dierd", "guid": "5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1", "addresses": ("mailto:dreid at example.com",) },
+ "nocalendar" : { "password": "radnelacon", "guid": "543D28BA-F74F-4D5F-9243-B3E3A61171E5", "addresses": () },
+ "user01" : { "password": "01user", "guid": None , "addresses": () },
+ "user02" : { "password": "02user", "guid": None , "addresses": () },
}
groups = {
@@ -83,6 +87,12 @@
xmlFile.copyTo(self._xmlFile)
return self._xmlFile
+ def augmentsFile(self):
+ if not hasattr(self, "_augmentsFile"):
+ self._augmentsFile = FilePath(self.mktemp())
+ augmentsFile.copyTo(self._augmentsFile)
+ return self._augmentsFile
+
class XMLFile (
XMLFileBase,
twistedcaldav.directory.test.util.BasicTestCase,
@@ -92,7 +102,9 @@
Test XML file based directory implementation.
"""
def service(self):
- return XMLDirectoryService(self.xmlFile())
+ directory = XMLDirectoryService(self.xmlFile())
+ augment.AugmentService = augment.AugmentXMLDB(xmlFiles=(self.augmentsFile().path,))
+ return directory
def test_changedXML(self):
service = self.service()
@@ -103,6 +115,7 @@
<accounts realm="Test Realm">
<user>
<uid>admin</uid>
+ <guid>admin</guid>
<password>nimda</password>
<name>Super User</name>
</user>
@@ -129,13 +142,28 @@
<accounts realm="Test Realm">
<location>
<uid>my office</uid>
+ <guid>myoffice</guid>
<password>nimda</password>
<name>Super User</name>
- <auto-schedule/>
</location>
</accounts>
"""
)
+ self.augmentsFile().open("w").write(
+"""<?xml version="1.0" encoding="utf-8"?>
+<!DOCTYPE accounts SYSTEM "accounts.dtd">
+<augments>
+ <record>
+ <guid>myoffice</guid>
+ <enable>true</enable>
+ <enable-calendar>true</enable-calendar>
+ <auto-schedule>true</auto-schedule>
+ </record>
+</augments>
+"""
+ )
+ augment.AugmentService.refresh()
+
for recordType, expectedRecords in (
( DirectoryService.recordType_users , () ),
( DirectoryService.recordType_groups , () ),
@@ -148,28 +176,7 @@
)
self.assertTrue(service.recordWithShortName(DirectoryService.recordType_locations, "my office").autoSchedule)
- def test_badAutoSchedule(self):
- service = self.service()
- self.xmlFile().open("w").write(
-"""<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE accounts SYSTEM "accounts.dtd">
-<accounts realm="Test Realm">
- <user>
- <uid>my office</uid>
- <password>nimda</password>
- <name>Super User</name>
- <auto-schedule/>
- </user>
-</accounts>
-"""
- )
-
- def _findRecords():
- set(r.shortName for r in service.listRecords(DirectoryService.recordType_users))
-
- self.assertRaises(ValueError, _findRecords)
-
def test_okDisableCalendar(self):
service = self.service()
@@ -186,11 +193,11 @@
<uid>disabled</uid>
<password>disabled</password>
<name>Disabled</name>
- <disable-calendar/>
</group>
</accounts>
"""
)
+
for recordType, expectedRecords in (
( DirectoryService.recordType_users , () ),
( DirectoryService.recordType_groups , ("enabled", "disabled") ),
@@ -201,88 +208,5 @@
set(r.shortName for r in service.listRecords(recordType)),
set(expectedRecords)
)
- self.assertTrue(service.recordWithShortName(DirectoryService.recordType_groups, "enabled").enabledForCalendaring)
+ self.assertFalse(service.recordWithShortName(DirectoryService.recordType_groups, "enabled").enabledForCalendaring)
self.assertFalse(service.recordWithShortName(DirectoryService.recordType_groups, "disabled").enabledForCalendaring)
-
- def test_badDisableCalendar(self):
- service = self.service()
-
- self.xmlFile().open("w").write(
-"""<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE accounts SYSTEM "accounts.dtd">
-<accounts realm="Test Realm">
- <location>
- <uid>my office</uid>
- <password>nimda</password>
- <name>Super User</name>
- <disable-calendar/>
- </location>
-</accounts>
-"""
- )
-
- def _findRecords():
- set(r.shortName for r in service.listRecords(DirectoryService.recordType_users))
-
- self.assertRaises(ValueError, _findRecords)
-
- def test_okProxies(self):
- service = self.service()
-
- self.xmlFile().open("w").write(
-"""<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE accounts SYSTEM "accounts.dtd">
-<accounts realm="Test Realm">
- <user>
- <uid>test</uid>
- <password>nimda</password>
- <name>Test</name>
- </user>
- <location>
- <uid>my office</uid>
- <password>nimda</password>
- <name>Super User</name>
- <auto-schedule/>
- <proxies>
- <member>test</member>
- </proxies>
- </location>
-</accounts>
-"""
- )
- for recordType, expectedRecords in (
- ( DirectoryService.recordType_users , ("test",) ),
- ( DirectoryService.recordType_groups , () ),
- ( DirectoryService.recordType_locations , ("my office",) ),
- ( DirectoryService.recordType_resources , () ),
- ):
- self.assertEquals(
- set(r.shortName for r in service.listRecords(recordType)),
- set(expectedRecords)
- )
- self.assertEqual(set([("users", "test",)],), service.recordWithShortName(DirectoryService.recordType_locations, "my office")._proxies)
- self.assertEqual(set([("locations", "my office",)],), service.recordWithShortName(DirectoryService.recordType_users, "test")._proxyFor)
-
- def test_badProxies(self):
- service = self.service()
-
- self.xmlFile().open("w").write(
-"""<?xml version="1.0" encoding="utf-8"?>
-<!DOCTYPE accounts SYSTEM "accounts.dtd">
-<accounts realm="Test Realm">
- <user>
- <uid>my office</uid>
- <password>nimda</password>
- <name>Super User</name>
- <proxies>
- <member>12345-GUID-67890</member>
- </proxies>
- </user>
-</accounts>
-"""
- )
-
- def _findRecords():
- set(r.shortName for r in service.listRecords(DirectoryService.recordType_users))
-
- self.assertRaises(ValueError, _findRecords)
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/util.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/util.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/test/util.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -14,17 +14,17 @@
# limitations under the License.
##
-import twisted.trial.unittest
from twisted.trial.unittest import SkipTest
from twisted.cred.credentials import UsernamePassword
from twisted.web2.auth.digest import DigestedCredentials, calcResponse, calcHA1
from twistedcaldav.directory.directory import DirectoryService
from twistedcaldav.directory.directory import UnknownRecordTypeError
+from twistedcaldav.test.util import TestCase
# FIXME: Add tests for GUID hooey, once we figure out what that means here
-class DirectoryTestCase (twisted.trial.unittest.TestCase):
+class DirectoryTestCase (TestCase):
"""
Tests a directory implementation.
"""
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/xmlaccountsparser.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/xmlaccountsparser.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/xmlaccountsparser.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -1,5 +1,5 @@
##
-# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+# Copyright (c) 2006-2009 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.
@@ -23,7 +23,6 @@
"XMLAccountsParser",
]
-from uuid import UUID
import xml.dom.minidom
from twisted.python.filepath import FilePath
@@ -43,18 +42,17 @@
ELEMENT_GUID = "guid"
ELEMENT_PASSWORD = "password"
ELEMENT_NAME = "name"
+ELEMENT_EMAIL_ADDRESS = "email-address"
ELEMENT_MEMBERS = "members"
ELEMENT_MEMBER = "member"
-ELEMENT_CUADDR = "cuaddr"
-ELEMENT_AUTOSCHEDULE = "auto-schedule"
-ELEMENT_DISABLECALENDAR = "disable-calendar"
-ELEMENT_PROXIES = "proxies"
-ELEMENT_READ_ONLY_PROXIES = "read-only-proxies"
ATTRIBUTE_REALM = "realm"
ATTRIBUTE_REPEAT = "repeat"
ATTRIBUTE_RECORDTYPE = "type"
+VALUE_TRUE = "true"
+VALUE_FALSE = "false"
+
RECORD_TYPES = {
ELEMENT_USER : DirectoryService.recordType_users,
ELEMENT_GROUP : DirectoryService.recordType_groups,
@@ -69,7 +67,8 @@
def __repr__(self):
return "<%s %r>" % (self.__class__.__name__, self.xmlFile)
- def __init__(self, xmlFile):
+ def __init__(self, xmlFile, externalUpdate=True):
+
if type(xmlFile) is str:
xmlFile = FilePath(xmlFile)
@@ -88,7 +87,7 @@
# Verify that top-level element is correct
accounts_node = doc._get_documentElement()
if accounts_node._get_localName() != ELEMENT_ACCOUNTS:
- self.log("Ignoring file %r because it is not a repository builder file" % (self.xmlFile,))
+ log.error("Ignoring file %r because it is not a repository builder file" % (self.xmlFile,))
return
self._parseXML(accounts_node)
@@ -103,23 +102,10 @@
def updateMembership(group):
# Update group membership
for recordType, shortName in group.members:
- item = self.items[recordType].get(shortName, None)
+ item = self.items[recordType].get(shortName)
if item is not None:
item.groups.add(group.shortName)
- def updateProxyFor(proxier):
- # Update proxy membership
- for recordType, shortName in proxier.proxies:
- item = self.items[recordType].get(shortName, None)
- if item is not None:
- item.proxyFor.add((proxier.recordType, proxier.shortName))
-
- # Update read-only proxy membership
- for recordType, shortName in proxier.readOnlyProxies:
- item = self.items[recordType].get(shortName, None)
- if item is not None:
- item.readOnlyProxyFor.add((proxier.recordType, proxier.shortName))
-
for child in node._get_childNodes():
child_name = child._get_localName()
if child_name is None:
@@ -148,7 +134,6 @@
for records in self.items.itervalues():
for principal in records.itervalues():
updateMembership(principal)
- updateProxyFor(principal)
class XMLAccountRecord (object):
"""
@@ -163,15 +148,9 @@
self.guid = None
self.password = None
self.name = None
+ self.emailAddresses = set()
self.members = set()
self.groups = set()
- self.calendarUserAddresses = set()
- self.autoSchedule = False
- self.enabledForCalendaring = True
- self.proxies = set()
- self.proxyFor = set()
- self.readOnlyProxies = set()
- self.readOnlyProxyFor = set()
def repeat(self, ctr):
"""
@@ -195,12 +174,12 @@
name = self.name % ctr
else:
name = self.name
- calendarUserAddresses = set()
- for cuaddr in self.calendarUserAddresses:
- if cuaddr.find("%") != -1:
- calendarUserAddresses.add(cuaddr % ctr)
+ emailAddresses = set()
+ for emailAddr in self.emailAddresses:
+ if emailAddr.find("%") != -1:
+ emailAddresses.add(emailAddr % ctr)
else:
- calendarUserAddresses.add(cuaddr)
+ emailAddresses.add(emailAddr)
result = XMLAccountRecord(self.recordType)
result.shortName = shortName
@@ -208,11 +187,6 @@
result.password = password
result.name = name
result.members = self.members
- result.calendarUserAddresses = calendarUserAddresses
- result.autoSchedule = self.autoSchedule
- result.enabledForCalendaring = self.enabledForCalendaring
- result.proxies = self.proxies
- result.readOnlyProxies = self.readOnlyProxies
return result
def parseXML(self, node):
@@ -225,43 +199,20 @@
self.shortName = child.firstChild.data.encode("utf-8")
elif child_name == ELEMENT_GUID:
if child.firstChild is not None:
- guid = child.firstChild.data.encode("utf-8")
- try:
- UUID(guid)
- except:
- log.error("Invalid GUID in accounts XML: %r" % (guid,))
- self.guid = guid
+ self.guid = child.firstChild.data.encode("utf-8")
+ if len(self.guid) < 4:
+ self.guid += "?" * (4 - len(self.guid))
elif child_name == ELEMENT_PASSWORD:
if child.firstChild is not None:
self.password = child.firstChild.data.encode("utf-8")
elif child_name == ELEMENT_NAME:
if child.firstChild is not None:
self.name = child.firstChild.data.encode("utf-8")
+ elif child_name == ELEMENT_EMAIL_ADDRESS:
+ if child.firstChild is not None:
+ self.emailAddresses.add(child.firstChild.data.encode("utf-8").lower())
elif child_name == ELEMENT_MEMBERS:
self._parseMembers(child, self.members)
- elif child_name == ELEMENT_CUADDR:
- if child.firstChild is not None:
- self.calendarUserAddresses.add(child.firstChild.data.encode("utf-8").lower())
- elif child_name == ELEMENT_AUTOSCHEDULE:
- # Only Resources & Locations
- if self.recordType not in (DirectoryService.recordType_resources, DirectoryService.recordType_locations):
- raise ValueError("<auto-schedule> element only allowed for Resources and Locations: %s" % (child_name,))
- self.autoSchedule = True
- elif child_name == ELEMENT_DISABLECALENDAR:
- # Only Users or Groups
- if self.recordType not in (DirectoryService.recordType_users, DirectoryService.recordType_groups):
- raise ValueError("<disable-calendar> element only allowed for Users or Groups: %s" % (child_name,))
- self.enabledForCalendaring = False
- elif child_name == ELEMENT_PROXIES:
- # Only Resources & Locations
- if self.recordType not in (DirectoryService.recordType_resources, DirectoryService.recordType_locations):
- raise ValueError("<auto-schedule> element only allowed for Resources and Locations: %s" % (child_name,))
- self._parseMembers(child, self.proxies)
- elif child_name == ELEMENT_READ_ONLY_PROXIES:
- # Only Resources & Locations
- if self.recordType not in (DirectoryService.recordType_resources, DirectoryService.recordType_locations):
- raise ValueError("<auto-schedule> element only allowed for Resources and Locations: %s" % (child_name,))
- self._parseMembers(child, self.readOnlyProxies)
else:
raise RuntimeError("Unknown account attribute: %s" % (child_name,))
Added: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/xmlaugmentsparser.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/xmlaugmentsparser.py (rev 0)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/xmlaugmentsparser.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -0,0 +1,141 @@
+##
+# Copyright (c) 2009 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.
+##
+
+from xml.etree.ElementTree import ElementTree
+from xml.parsers.expat import ExpatError
+import types
+
+"""
+XML based augment configuration file handling.
+"""
+
+__all__ = [
+ "XMLAugmentsParser",
+]
+
+from twistedcaldav.log import Logger
+
+log = Logger()
+
+ELEMENT_AUGMENTS = "augments"
+ELEMENT_RECORD = "record"
+
+ELEMENT_GUID = "guid"
+ELEMENT_ENABLE = "enable"
+ELEMENT_HOSTEDAT = "hosted-at"
+ELEMENT_ENABLECALENDAR = "enable-calendar"
+ELEMENT_AUTOSCHEDULE = "auto-schedule"
+ELEMENT_CUADDR = "cuaddr"
+
+ATTRIBUTE_REPEAT = "repeat"
+
+VALUE_TRUE = "true"
+VALUE_FALSE = "false"
+
+ELEMENT_AUGMENTRECORD_MAP = {
+ ELEMENT_GUID: "guid",
+ ELEMENT_ENABLE: "enabled",
+ ELEMENT_HOSTEDAT: "hostedAt",
+ ELEMENT_ENABLECALENDAR: "enabledForCalendaring",
+ ELEMENT_AUTOSCHEDULE: "autoSchedule",
+ ELEMENT_CUADDR: "calendarUserAddresses",
+}
+
+class XMLAugmentsParser(object):
+ """
+ XML augments configuration file parser.
+ """
+ def __repr__(self):
+ return "<%s %r>" % (self.__class__.__name__, self.xmlFile)
+
+ def __init__(self, xmlFile, items):
+
+ self.items = items
+ self.xmlFile = xmlFile
+
+ # Read in XML
+ try:
+ tree = ElementTree(file=self.xmlFile)
+ except ExpatError, e:
+ log.error("Unable to parse file '%s' because: %s" % (self.xmlFile, e,), raiseException=RuntimeError)
+
+ # Verify that top-level element is correct
+ augments_node = tree.getroot()
+ if augments_node.tag != ELEMENT_AUGMENTS:
+ log.error("Ignoring file '%s' because it is not a augments file" % (self.xmlFile,), raiseException=RuntimeError)
+
+ self._parseXML(augments_node)
+
+ def _parseXML(self, rootnode):
+ """
+ Parse the XML root node from the augments configuration document.
+ @param rootnode: the L{Element} to parse.
+ """
+ for child in rootnode.getchildren():
+
+ if child.tag != ELEMENT_RECORD:
+ log.error("Unknown augment type: '%s' in augment file: '%s'" % (child.tag, self.xmlFile,), raiseException=RuntimeError)
+
+ repeat = int(child.get(ATTRIBUTE_REPEAT, "1"))
+
+ fields = {}
+ for node in child.getchildren():
+
+ if node.tag in (
+ ELEMENT_GUID,
+ ELEMENT_HOSTEDAT,
+ ):
+ fields[node.tag] = node.text
+ elif node.tag in (
+ ELEMENT_ENABLE,
+ ELEMENT_ENABLECALENDAR,
+ ELEMENT_AUTOSCHEDULE,
+ ):
+ fields[node.tag] = node.text == VALUE_TRUE
+ elif node.tag == ELEMENT_CUADDR:
+ fields.setdefault(node.tag, set()).add(node.text)
+ else:
+ log.error("Invalid element '%s' in augment file: '%s'" % (node.tag, self.xmlFile,), raiseException=RuntimeError)
+
+ # Must have at least a guid
+ if ELEMENT_GUID not in fields:
+ log.error("Invalid record '%s' without a guid in augment file: '%s'" % (child, self.xmlFile,), raiseException=RuntimeError)
+
+ if repeat > 1:
+ for i in xrange(1, repeat+1):
+ self.buildRecord(fields, i)
+ else:
+ self.buildRecord(fields)
+
+ def buildRecord(self, fields, count=None):
+
+ from twistedcaldav.directory.augment import AugmentRecord
+
+ def expandCount(value, count):
+
+ if type(value) in types.StringTypes:
+ return value % (count,) if count and "%" in value else value
+ elif type(value) == set:
+ return set([item % (count,) if count and "%" in item else item for item in value])
+ else:
+ return value
+
+ actualFields = {}
+ for k,v in fields.iteritems():
+ actualFields[ELEMENT_AUGMENTRECORD_MAP[k]] = expandCount(v, count)
+
+ record = AugmentRecord(**actualFields)
+ self.items[record.guid] = record
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/xmlfile.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/xmlfile.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/directory/xmlfile.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -1,5 +1,5 @@
##
-# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+# Copyright (c) 2006-2009 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.
@@ -26,6 +26,7 @@
from twisted.web2.auth.digest import DigestedCredentials
from twisted.python.filepath import FilePath
+from twistedcaldav.directory import augment
from twistedcaldav.directory.directory import DirectoryService, DirectoryRecord
from twistedcaldav.directory.xmlaccountsparser import XMLAccountsParser
@@ -61,23 +62,39 @@
def listRecords(self, recordType):
for entryShortName, xmlPrincipal in self._entriesForRecordType(recordType):
- yield XMLDirectoryRecord(
+ record = XMLDirectoryRecord(
service = self,
recordType = recordType,
shortName = entryShortName,
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.
+ d = augment.AugmentService.getAugmentRecord(record.guid)
+ d.addCallback(lambda x:record.addAugmentInformation(x))
+
+ yield record
+
def recordWithShortName(self, recordType, shortName):
for entryShortName, xmlprincipal in self._entriesForRecordType(recordType):
if entryShortName == shortName:
- return XMLDirectoryRecord(
+ record = XMLDirectoryRecord(
service = self,
recordType = recordType,
shortName = entryShortName,
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.
+ d = augment.AugmentService.getAugmentRecord(record.guid)
+ d.addCallback(lambda x:record.addAugmentInformation(x))
+
+ return record
+
return None
def _entriesForRecordType(self, recordType):
@@ -108,18 +125,12 @@
guid = xmlPrincipal.guid,
shortName = shortName,
fullName = xmlPrincipal.name,
- calendarUserAddresses = xmlPrincipal.calendarUserAddresses,
- autoSchedule = xmlPrincipal.autoSchedule,
- enabledForCalendaring = xmlPrincipal.enabledForCalendaring,
+ emailAddresses = xmlPrincipal.emailAddresses,
)
self.password = xmlPrincipal.password
self._members = xmlPrincipal.members
self._groups = xmlPrincipal.groups
- self._proxies = xmlPrincipal.proxies
- self._proxyFor = xmlPrincipal.proxyFor
- self._readOnlyProxies = xmlPrincipal.readOnlyProxies
- self._readOnlyProxyFor = xmlPrincipal.readOnlyProxyFor
def members(self):
for recordType, shortName in self._members:
@@ -129,26 +140,11 @@
for shortName in self._groups:
yield self.service.recordWithShortName(DirectoryService.recordType_groups, shortName)
- def proxies(self):
- for recordType, shortName in self._proxies:
- yield self.service.recordWithShortName(recordType, shortName)
-
- def proxyFor(self, read_write=True):
- for recordType, shortName in self._proxyFor:
- yield self.service.recordWithShortName(recordType, shortName)
-
- def readOnlyProxies(self):
- for recordType, shortName in self._readOnlyProxies:
- yield self.service.recordWithShortName(recordType, shortName)
-
- def readOnlyProxyFor(self, read_write=True):
- for recordType, shortName in self._readOnlyProxyFor:
- yield self.service.recordWithShortName(recordType, shortName)
-
def verifyCredentials(self, credentials):
- if isinstance(credentials, UsernamePassword):
- return credentials.password == self.password
- if isinstance(credentials, DigestedCredentials):
- return credentials.checkPassword(self.password)
+ if self.enabled:
+ if isinstance(credentials, UsernamePassword):
+ return credentials.password == self.password
+ if isinstance(credentials, DigestedCredentials):
+ return credentials.checkPassword(self.password)
return super(XMLDirectoryRecord, self).verifyCredentials(credentials)
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/log.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/log.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/log.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -1,5 +1,5 @@
##
-# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+# Copyright (c) 2006-2009 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.
@@ -224,8 +224,10 @@
#
# Attach methods to Logger
#
- def log_emit(self, message, level=level, **kwargs):
+ def log_emit(self, message, level=level, raiseException=None, **kwargs):
self.emit(level, message, **kwargs)
+ if raiseException:
+ raise raiseException(message)
log_emit.__doc__ = doc
@@ -234,8 +236,10 @@
#
# Attach methods to LoggingMixIn
#
- def log_emit(self, message, level=level, **kwargs):
+ def log_emit(self, message, level=level, raiseException=None, **kwargs):
self.logger.emit(level, message, **kwargs)
+ if raiseException:
+ raise raiseException(message)
log_emit.__doc__ = doc
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/method/report_common.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/method/report_common.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/method/report_common.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -284,7 +284,7 @@
_namedPropertiesForResource = deferredGenerator(_namedPropertiesForResource)
def generateFreeBusyInfo(request, calresource, fbinfo, timerange, matchtotal,
- excludeuid=None, organizer=None, same_calendar_user=False):
+ excludeuid=None, organizer=None, organizerPrincipal=None, same_calendar_user=False):
"""
Run a free busy report on the specified calendar collection
accumulating the free busy info for later processing.
@@ -303,7 +303,7 @@
# First check the privilege on this collection
try:
- d = waitForDeferred(calresource.checkPrivileges(request, (caldavxml.ReadFreeBusy(),)))
+ d = waitForDeferred(calresource.checkPrivileges(request, (caldavxml.ReadFreeBusy(),), principal=organizerPrincipal))
yield d
d.getResult()
except AccessDeniedError:
@@ -354,7 +354,7 @@
child = child.getResult()
try:
- d = waitForDeferred(child.checkPrivileges(request, (caldavxml.ReadFreeBusy(),), inherited_aces=filteredaces))
+ d = waitForDeferred(child.checkPrivileges(request, (caldavxml.ReadFreeBusy(),), inherited_aces=filteredaces, principal=organizerPrincipal))
yield d
d.getResult()
except AccessDeniedError:
Added: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/partitions.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/partitions.py (rev 0)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/partitions.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -0,0 +1,56 @@
+##
+# Copyright (c) 2009 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.
+##
+
+from twistedcaldav.log import Logger
+from twistedcaldav.py.plistlib import readPlist
+
+"""
+Collection of classes for managing partition information for a group of servers.
+"""
+
+log = Logger()
+
+class Partitions(object):
+
+ def __init__(self):
+
+ self.clear()
+
+ def clear(self):
+ self.partitions = {}
+ self.ownUID = ""
+
+ def readConfig(self, plistpath):
+ try:
+ dataDict = readPlist(plistpath)
+ except (IOError, OSError):
+ log.error("Configuration file does not exist or is inaccessible: %s" % (self._configFileName,))
+ return
+
+ for partition in dataDict.get("partitions", ()):
+ uid = partition.get("uid", None)
+ url = partition.get("url", None)
+ if uid and url:
+ self.partitions[uid] = url
+
+ def setSelfPartition(self, uid):
+ self.ownUID = uid
+
+ def getPartitionURL(self, uid):
+ # When the UID matches this server return an empty string
+ return self.partitions.get(uid, None) if uid != self.ownUID else ""
+
+partitions = Partitions()
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/sql.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/sql.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/sql.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -172,7 +172,7 @@
self._db_init_data_tables(q)
self._db_recreate()
- q.execute("commit")
+ #q.execute("commit")
self._db_connection.isolation_level = old_isolation
def _db_init_schema_table(self, q):
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/static.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/static.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/static.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -37,6 +37,7 @@
import os
import errno
from urlparse import urlsplit
+import urlparse
from twisted.internet.defer import fail, succeed, inlineCallbacks, returnValue
from twisted.python.failure import Failure
@@ -48,7 +49,8 @@
from twisted.web2.dav.idav import IDAVResource
from twisted.web2.dav.resource import AccessDeniedError
from twisted.web2.dav.resource import davPrivilegeSet
-from twisted.web2.dav.util import parentForURL, bindMethods
+from twisted.web2.dav.util import parentForURL, bindMethods, joinURL
+from twisted.web2.resource import RedirectResource
from twistedcaldav import caldavxml
from twistedcaldav import customxml
@@ -597,60 +599,78 @@
assert len(name) > 4, "Directory record has an invalid GUID: %r" % (name,)
- childPath = self.fp.child(name[0:2]).child(name[2:4]).child(name)
- child = self.homeResourceClass(childPath.path, self, record)
-
- if not child.exists():
- self.provision()
-
- if not childPath.parent().isdir():
- childPath.parent().makedirs()
-
- for oldPath in (
- # Pre 2.0: All in one directory
- self.fp.child(name),
- # Pre 1.2: In types hierarchy instead of the GUID hierarchy
- self.parent.getChild(record.recordType).fp.child(record.shortName),
- ):
- if oldPath.exists():
- # The child exists at an old location. Move to new location.
- log.msg("Moving calendar home from old location %r to new location %r." % (oldPath, childPath))
- try:
- oldPath.moveTo(childPath)
- except (OSError, IOError), e:
- log.err("Error moving calendar home %r: %s" % (oldPath, e))
+ if record.locallyHosted():
+ childPath = self.fp.child(name[0:2]).child(name[2:4]).child(name)
+ child = self.homeResourceClass(childPath.path, self, record)
+
+ if not child.exists():
+ self.provision()
+
+ if not childPath.parent().isdir():
+ childPath.parent().makedirs()
+
+ for oldPath in (
+ # Pre 2.0: All in one directory
+ self.fp.child(name),
+ # Pre 1.2: In types hierarchy instead of the GUID hierarchy
+ self.parent.getChild(record.recordType).fp.child(record.shortName),
+ ):
+ if oldPath.exists():
+ # The child exists at an old location. Move to new location.
+ log.msg("Moving calendar home from old location %r to new location %r." % (oldPath, childPath))
+ try:
+ oldPath.moveTo(childPath)
+ except (OSError, IOError), e:
+ log.err("Error moving calendar home %r: %s" % (oldPath, e))
+ raise HTTPError(StatusResponse(
+ responsecode.INTERNAL_SERVER_ERROR,
+ "Unable to move calendar home."
+ ))
+ child.fp.restat(False)
+ break
+ else:
+ #
+ # NOTE: provisionDefaultCalendars() returns a deferred, which we are ignoring.
+ # The result being that the default calendars will be present at some point
+ # in the future, not necessarily right now, and we don't have a way to wait
+ # on that to finish.
+ #
+ child.provisionDefaultCalendars()
+
+ #
+ # Try to work around the above a little by telling the client that something
+ # when wrong temporarily if the child isn't provisioned right away.
+ #
+ if not child.exists():
raise HTTPError(StatusResponse(
- responsecode.INTERNAL_SERVER_ERROR,
- "Unable to move calendar home."
+ responsecode.SERVICE_UNAVAILABLE,
+ "Provisioning calendar home."
))
- child.fp.restat(False)
- break
- else:
- #
- # NOTE: provisionDefaultCalendars() returns a deferred, which we are ignoring.
- # The result being that the default calendars will be present at some point
- # in the future, not necessarily right now, and we don't have a way to wait
- # on that to finish.
- #
- child.provisionDefaultCalendars()
+
+ assert child.exists()
+
+ else:
+ childPath = self.fp.child(name[0:2]).child(name[2:4]).child(name)
+ child = CalendarHomeRedirectFile(childPath.path, self, record)
- #
- # Try to work around the above a little by telling the client that something
- # when wrong temporarily if the child isn't provisioned right away.
- #
- if not child.exists():
- raise HTTPError(StatusResponse(
- responsecode.SERVICE_UNAVAILABLE,
- "Provisioning calendar home."
- ))
-
- assert child.exists()
-
return child
def createSimilarFile(self, path):
raise HTTPError(responsecode.NOT_FOUND)
+class CalendarHomeRedirectFile(RedirectResource):
+
+ def __init__(self, path, parent, record):
+ self.path = path
+ self.parent = parent
+ self.record = record
+
+ parsedURL = urlparse.urlparse(self.record.hostedURL())
+ super(CalendarHomeRedirectFile, self).__init__(scheme=parsedURL.scheme, host=parsedURL.hostname, port=parsedURL.port)
+
+ def url(self):
+ return joinURL(self.parent.url(), self.record.guid)
+
class CalendarHomeFile (PropfindCacheMixin, AutoProvisioningFileMixIn, DirectoryCalendarHomeResource, CalDAVFile):
"""
Calendar home collection resource.
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/tap.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/tap.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/tap.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -19,6 +19,7 @@
from zope.interface import implements
+from twisted.internet import reactor
from twisted.internet.address import IPv4Address
from twisted.python.log import FileLogObserver
@@ -45,6 +46,8 @@
from twistedcaldav.config import config, parseConfig, defaultConfig, ConfigurationError
from twistedcaldav.root import RootResource
from twistedcaldav.resource import CalDAVResource
+from twistedcaldav.directory import augment
+from twistedcaldav.directory.calendaruserproxyloader import XMLCalendarUserProxyLoader
from twistedcaldav.directory.digest import QopDigestCredentialFactory
from twistedcaldav.directory.principal import DirectoryPrincipalProvisioningResource
from twistedcaldav.directory.aggregate import AggregateDirectoryService
@@ -446,6 +449,19 @@
setLogLevelForNamespace(None, "info")
#
+ # Setup the Augment Service
+ #
+ augmentClass = namedClass(config.AugmentService.type)
+
+ log.info("Configuring augment service of type: %s" % (augmentClass,))
+
+ try:
+ augment.AugmentService = augmentClass(**config.AugmentService.params)
+ except IOError, e:
+ log.error("Could not start augment service")
+ raise
+
+ #
# Setup the Directory
#
directories = []
@@ -481,6 +497,16 @@
SudoDirectoryService.recordType_sudoers)
#
+ # Make sure proxies get initialized
+ #
+ if config.ProxyLoadFromFile:
+ def _doProxyUpdate():
+ loader = XMLCalendarUserProxyLoader(config.ProxyLoadFromFile)
+ return loader.updateProxyDB()
+
+ reactor.addSystemEventTrigger("before", "startup", _doProxyUpdate)
+
+ #
# Configure Memcached Client Pool
#
if config.Memcached["ClientEnabled"]:
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_cache.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_cache.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_cache.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -18,7 +18,6 @@
import hashlib
import cPickle
-from twisted.trial.unittest import TestCase
from twisted.internet.defer import succeed, maybeDeferred
from twisted.web2.dav import davxml
@@ -31,6 +30,7 @@
from twistedcaldav.cache import PropfindCacheMixin
from twistedcaldav.test.util import InMemoryMemcacheProtocol
+from twistedcaldav.test.util import TestCase
def _newCacheToken(self):
@@ -87,6 +87,7 @@
class MemCacheChangeNotifierTests(TestCase):
def setUp(self):
+ TestCase.setUp(self)
self.memcache = InMemoryMemcacheProtocol()
self.ccn = MemcacheChangeNotifier(
StubURLResource(':memory:'),
@@ -438,6 +439,7 @@
Test the PropfindCacheMixin
"""
def setUp(self):
+ TestCase.setUp(self)
self.resource = TestCachingResource(StubResponse(200, {}, "foobar"))
self.responseCache = StubResponseCacheResource()
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_config.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_config.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_config.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -158,7 +158,6 @@
self.assertEquals(config.DirectoryService.type, "twistedcaldav.directory.appleopendirectory.OpenDirectoryService")
self.assertNotIn("xmlFile", config.DirectoryService.params)
self.assertEquals(config.DirectoryService.params.node, "/Search")
- self.assertEquals(config.DirectoryService.params.requireComputerRecord, True)
self.assertEquals(config.DirectoryService.params.cacheTimeout, 30)
def testDirectoryService_newParam(self):
@@ -167,13 +166,11 @@
config.update({"DirectoryService": {"type": "twistedcaldav.directory.appleopendirectory.OpenDirectoryService"}})
config.update({"DirectoryService": {"params": {
- "requireComputerRecord": False,
"cacheTimeout": 12345,
}}})
self.assertEquals(config.DirectoryService.type, "twistedcaldav.directory.appleopendirectory.OpenDirectoryService")
self.assertEquals(config.DirectoryService.params.node, "/Search")
- self.assertEquals(config.DirectoryService.params.requireComputerRecord, False)
self.assertEquals(config.DirectoryService.params.cacheTimeout, 12345)
def testDirectoryService_unknownType(self):
Added: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_database.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_database.py (rev 0)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_database.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -0,0 +1,208 @@
+##
+# Copyright (c) 2009 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.
+##
+
+from twistedcaldav.database import AbstractADBAPIDatabase
+import twistedcaldav.test.util
+
+from twisted.internet.defer import inlineCallbacks
+
+import os
+import time
+
+class Database (twistedcaldav.test.util.TestCase):
+ """
+ Test abstract SQL DB class
+ """
+
+ class TestDB(AbstractADBAPIDatabase):
+
+ def __init__(self, path, persistent=False, version="1"):
+ self.version = version
+ self.dbpath = path
+ super(Database.TestDB, self).__init__("sqlite", "sqlite3", (path,), persistent, cp_min=3, cp_max=3)
+
+ def _db_version(self):
+ """
+ @return: the schema version assigned to this index.
+ """
+ return self.version
+
+ def _db_type(self):
+ """
+ @return: the collection type assigned to this index.
+ """
+ return "TESTTYPE"
+
+ def _db_init_data_tables(self):
+ """
+ Initialise the underlying database tables.
+ @param q: a database cursor to use.
+ """
+
+ #
+ # TESTTYPE table
+ #
+ return self._db_execute(
+ """
+ create table TESTTYPE (
+ KEY text unique,
+ VALUE text
+ )
+ """
+ )
+
+ def _db_remove_data_tables(self):
+ return self._db_execute("drop table TESTTYPE")
+
+ class TestDBRecreateUpgrade(TestDB):
+
+ class RecreateDBException(Exception):
+ pass
+ class UpgradeDBException(Exception):
+ pass
+
+ def __init__(self, path, persistent=False):
+ super(Database.TestDBRecreateUpgrade, self).__init__(path, persistent, version="2")
+
+ def _db_recreate(self):
+ raise self.RecreateDBException()
+
+ class TestDBCreateIndexOnUpgrade(TestDB):
+
+ def __init__(self, path, persistent=False):
+ super(Database.TestDBCreateIndexOnUpgrade, self).__init__(path, persistent, version="2")
+
+ def _db_upgrade_data_tables(self, old_version):
+ return self._db_execute(
+ """
+ create index TESTING on TESTTYPE (VALUE)
+ """
+ )
+
+ class TestDBPauseInInit(TestDB):
+
+ def _db_init(self):
+
+ time.sleep(1)
+ super(Database.TestDBPauseInInit, self)._db_init()
+
+ @inlineCallbacks
+ def inlineCallbackRaises(self, exc, f, *args, **kwargs):
+ try:
+ yield f(*args, **kwargs)
+ except exc:
+ pass
+ except Exception, e:
+ self.fail("Wrong exception raised: %s" % (e,))
+ else:
+ self.fail("%s not raised" % (exc,))
+
+ @inlineCallbacks
+ def test_connect(self):
+ """
+ Connect to database and create table
+ """
+ db = Database.TestDB(self.mktemp())
+ self.assertFalse(db.initialized)
+ yield db.open()
+ self.assertTrue(db.initialized)
+
+ @inlineCallbacks
+ def test_readwrite(self):
+ """
+ Add a record, search for it
+ """
+ db = Database.TestDB(self.mktemp())
+ yield db.execute("INSERT into TESTTYPE (KEY, VALUE) values (:1, :2)", ("FOO", "BAR",))
+ items = (yield db.query("SELECT * from TESTTYPE"))
+ self.assertEqual(items, (("FOO", "BAR"),))
+ items = (yield db.queryList("SELECT * from TESTTYPE"))
+ self.assertEqual(items, ("FOO",))
+
+ @inlineCallbacks
+ def test_close(self):
+ """
+ Close database
+ """
+ db = Database.TestDB(self.mktemp())
+ self.assertFalse(db.initialized)
+ yield db.open()
+ db.close()
+ self.assertFalse(db.initialized)
+ db.close()
+
+ @inlineCallbacks
+ def test_version_upgrade_nonpersistent(self):
+ """
+ Connect to database and create table
+ """
+
+ db_file = self.mktemp()
+
+ db = Database.TestDB(db_file)
+ yield db.open()
+ yield db.execute("INSERT into TESTTYPE (KEY, VALUE) values (:1, :2)", ("FOO", "BAR",))
+ items = (yield db.query("SELECT * from TESTTYPE"))
+ self.assertEqual(items, (("FOO", "BAR"),))
+ db.close()
+ db = None
+
+ db = Database.TestDBRecreateUpgrade(db_file)
+ yield self.inlineCallbackRaises(Database.TestDBRecreateUpgrade.RecreateDBException, db.open)
+ items = (yield db.query("SELECT * from TESTTYPE"))
+ self.assertEqual(items, ())
+
+ def test_version_upgrade_persistent(self):
+ """
+ Connect to database and create table
+ """
+ db_file = self.mktemp()
+ db = Database.TestDB(db_file, persistent=True)
+ yield db.open()
+ yield db.execute("INSERT into TESTTYPE (KEY, VALUE) values (:1, :2)", "FOO", "BAR")
+ items = (yield db.query("SELECT * from TESTTYPE"))
+ self.assertEqual(items, (("FOO", "BAR")))
+ db.close()
+ db = None
+
+ db = Database.TestDBRecreateUpgrade(db_file, persistent=True)
+ yield self.inlineCallbackRaises(NotImplementedError, db.open)
+ self.assertTrue(os.path.exists(db_file))
+ db.close()
+ db = None
+
+ db = Database.TestDB(db_file, persistent=True, autocommit=True)
+ yield db.open()
+ items = (yield db.query("SELECT * from TESTTYPE"))
+ self.assertEqual(items, (("FOO", "BAR")))
+
+ def test_version_upgrade_persistent_add_index(self):
+ """
+ Connect to database and create table
+ """
+ db_file = self.mktemp()
+ db = Database.TestDB(db_file, persistent=True, autocommit=True)
+ yield db.open()
+ yield db.execute("INSERT into TESTTYPE (KEY, VALUE) values (:1, :2)", "FOO", "BAR")
+ items = (yield db.query("SELECT * from TESTTYPE"))
+ self.assertEqual(items, (("FOO", "BAR")))
+ db.close()
+ db = None
+
+ db = Database.TestDBCreateIndexOnUpgrade(db_file, persistent=True, autocommit=True)
+ yield db.open()
+ items = (yield db.query("SELECT * from TESTTYPE"))
+ self.assertEqual(items, (("FOO", "BAR")))
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_log.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_log.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_log.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -17,7 +17,7 @@
from twistedcaldav.log import *
from twistedcaldav.log import logLevelsByNamespace
-import twisted.trial.unittest
+from twistedcaldav.test.util import TestCase
defaultLogLevel = logLevelsByNamespace[None]
@@ -37,7 +37,7 @@
class LoggingEnabledObject (LoggingMixIn):
pass
-class Logging (twisted.trial.unittest.TestCase):
+class Logging (TestCase):
def test_cmpLogLevels(self):
self.assertEquals(cmpLogLevels("info" , "error"), -1)
self.assertEquals(cmpLogLevels("debug", "debug"), 0)
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_memcache.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_memcache.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_memcache.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -7,14 +7,12 @@
from twistedcaldav.memcache import MemCacheProtocol, NoSuchCommand
from twistedcaldav.memcache import ClientError, ServerError
+from twistedcaldav.test.util import TestCase
-from twisted.trial.unittest import TestCase
from twisted.test.proto_helpers import StringTransportWithDisconnection
from twisted.internet.task import Clock
from twisted.internet.defer import Deferred, gatherResults, TimeoutError
-
-
class MemCacheTestCase(TestCase):
"""
Test client protocol class L{MemCacheProtocol}.
@@ -25,6 +23,7 @@
Create a memcache client, connect it to a string protocol, and make it
use a deterministic clock.
"""
+ TestCase.setUp(self)
self.proto = MemCacheProtocol()
self.clock = Clock()
self.proto.callLater = self.clock.callLater
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_memcachepool.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_memcachepool.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_memcachepool.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -16,8 +16,6 @@
from zope.interface import implements
-from twisted.trial.unittest import TestCase
-
from twisted.internet.interfaces import IConnector, IReactorTCP
from twisted.internet.address import IPv4Address
@@ -25,8 +23,8 @@
from twistedcaldav.memcachepool import PooledMemCacheProtocol
from twistedcaldav.memcachepool import MemCacheClientFactory
from twistedcaldav.memcachepool import MemCachePool
+from twistedcaldav.test.util import TestCase
-
MC_ADDRESS = IPv4Address('TCP', '127.0.0.1', 11211)
@@ -137,6 +135,7 @@
Create a L{MemCacheClientFactory} instance and and give it a
L{StubConnectionPool} instance.
"""
+ TestCase.setUp(self)
self.pool = StubConnectionPool()
self.factory = MemCacheClientFactory()
self.factory.connectionPool = self.pool
@@ -196,6 +195,7 @@
"""
Create a L{MemCachePool}.
"""
+ TestCase.setUp(self)
self.reactor = StubReactor()
self.pool = MemCachePool(MC_ADDRESS,
maxClients=5,
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_memcacher.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_memcacher.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_memcacher.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -6,10 +6,10 @@
"""
from twisted.internet.defer import inlineCallbacks
-from twisted.trial.unittest import TestCase
from twistedcaldav.config import config
from twistedcaldav.memcacher import Memcacher
+from twistedcaldav.test.util import TestCase
class MemcacherTestCase(TestCase):
"""
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_resource.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_resource.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_resource.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -14,13 +14,11 @@
# limitations under the License.
##
-from twisted.trial.unittest import TestCase
-
from twistedcaldav.resource import CalDAVResource
from twistedcaldav.test.util import InMemoryPropertyStore
+from twistedcaldav.test.util import TestCase
-
class StubProperty(object):
def qname(self):
return "StubQname"
@@ -28,6 +26,7 @@
class CalDAVResourceTests(TestCase):
def setUp(self):
+ TestCase.setUp(self)
self.resource = CalDAVResource()
self.resource._dead_properties = InMemoryPropertyStore()
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_root.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_root.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_root.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -18,9 +18,10 @@
from twistedcaldav.root import RootResource
from twistedcaldav.test.util import TestCase
+from twistedcaldav.directory import augment
from twistedcaldav.directory.principal import DirectoryPrincipalProvisioningResource
from twistedcaldav.directory.xmlfile import XMLDirectoryService
-from twistedcaldav.directory.test.test_xmlfile import xmlFile
+from twistedcaldav.directory.test.test_xmlfile import xmlFile, augmentsFile
from twisted.cred.portal import Portal
@@ -52,6 +53,7 @@
class RootTests(TestCase):
def setUp(self):
+ TestCase.setUp(self)
self.docroot = self.mktemp()
os.mkdir(self.docroot)
@@ -59,6 +61,7 @@
'calendar': ['dreid']})
directory = XMLDirectoryService(xmlFile)
+ augment.AugmentService = augment.AugmentXMLDB(xmlFiles=(augmentsFile.path,))
principals = DirectoryPrincipalProvisioningResource('/principals/', directory)
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_static.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_static.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_static.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -14,11 +14,10 @@
# limitations under the License.
##
-from twisted.trial.unittest import TestCase
-
from twistedcaldav.static import CalendarHomeFile, CalDAVFile
from twistedcaldav.cache import DisabledCacheNotifier
from twistedcaldav.test.util import StubCacheChangeNotifier
+from twistedcaldav.test.util import TestCase
class StubParentResource(object):
def principalCollections(self):
@@ -27,6 +26,7 @@
class CalendarHomeFileTests(TestCase):
def setUp(self):
+ TestCase.setUp(self)
self.calendarHome = CalendarHomeFile(self.mktemp(),
StubParentResource(),
object())
@@ -44,6 +44,7 @@
class CalDAVFileTests(TestCase):
def setUp(self):
+ TestCase.setUp(self)
self.caldavFile = CalDAVFile(self.mktemp())
self.caldavFile.fp.createDirectory()
self.caldavFile.cacheNotifier = StubCacheChangeNotifier()
Modified: CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_tap.py
===================================================================
--- CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_tap.py 2009-09-08 19:12:31 UTC (rev 4528)
+++ CalendarServer/branches/users/cdaboo/deployment-partition-4524/twistedcaldav/test/test_tap.py 2009-09-08 20:38:33 UTC (rev 4529)
@@ -17,8 +17,6 @@
import os
from copy import deepcopy
-from twisted.trial import unittest
-
from twisted.python.usage import Options, UsageError
from twisted.python.util import sibpath
from twisted.python.reflect import namedAny
@@ -39,6 +37,7 @@
from twistedcaldav.directory.sudo import SudoDirectoryService
from twistedcaldav.directory.directory import UnknownRecordTypeError
+from twistedcaldav.test.util import TestCase
class TestCalDAVOptions(CalDAVOptions):
"""
@@ -53,7 +52,7 @@
pass
-class CalDAVOptionsTest(unittest.TestCase):
+class CalDAVOptionsTest(TestCase):
"""
Test various parameters of our usage.Options subclass
"""
@@ -63,6 +62,7 @@
Set up our options object, giving it a parent, and forcing the
global config to be loaded from defaults.
"""
+ TestCase.setUp(self)
self.config = TestCalDAVOptions()
self.config.parent = Options()
self.config.parent['uid'] = 0
@@ -163,7 +163,7 @@
self.assertEquals(config.MultiProcess['ProcessCount'], 102)
-class BaseServiceMakerTests(unittest.TestCase):
+class BaseServiceMakerTests(TestCase):
"""
Utility class for ServiceMaker tests.
"""
@@ -171,6 +171,7 @@
configOptions = None
def setUp(self):
+ TestCase.setUp(self)
self.options = TestCalDAVOptions()
self.options.parent = Options()
self.options.parent['gid'] = None
@@ -181,12 +182,19 @@
accountsFile = sibpath(os.path.dirname(__file__),
'directory/test/accounts.xml')
+ augmentsFile = sibpath(os.path.dirname(__file__),
+ 'directory/test/augments.xml')
self.config['DirectoryService'] = {
'params': {'xmlFile': accountsFile},
'type': 'twistedcaldav.directory.xmlfile.XMLDirectoryService'
}
+ self.config["AugmentService"] = {
+ "params": {"xmlFiles": [augmentsFile]},
+ "type": "twistedcaldav.directory.augment.AugmentXMLDB"
+ }
+
self.config['DocumentRoot'] = self.mktemp()
self.config['DataRoot'] = self.mktemp()
self.config['ProcessType'] = 'Slave'
@@ -195,6 +203,9 @@
self.config['SudoersFile'] = ''
+ self.config['Memcached']['ClientEnabled'] = False
+ self.config['Memcached']['ServerEnabled'] = False
+
if self.configOptions:
config_mod._mergeData(self.config, self.configOptions)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20090908/dc1c5329/attachment-0001.html>
More information about the calendarserver-changes
mailing list