<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" /><style type="text/css"><!--
#msg dl { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre, #msg p { overflow: auto; background: #ffc; border: 1px #fc0 solid; padding: 6px; }
#msg ul { overflow: auto; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<title>[2427] CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425</title>
</head>
<body>

<div id="msg">
<dl>
<dt>Revision</dt> <dd><a href="http://trac.macosforge.org/projects/calendarserver/changeset/2427">2427</a></dd>
<dt>Author</dt> <dd>cdaboo@apple.com</dd>
<dt>Date</dt> <dd>2008-05-18 18:28:17 -0700 (Sun, 18 May 2008)</dd>
</dl>

<h3>Log Message</h3>
<pre>Add support for directory based read-only delegates for locations and resources.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServerbranchesuserscdabooreadonlydirectoryproxy2425confaccountstestxml">CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/conf/accounts-test.xml</a></li>
<li><a href="#CalendarServerbranchesuserscdabooreadonlydirectoryproxy2425confaccountsdtd">CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/conf/accounts.dtd</a></li>
<li><a href="#CalendarServerbranchesuserscdabooreadonlydirectoryproxy2425twistedcaldavdirectoryappleopendirectorypy">CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/appleopendirectory.py</a></li>
<li><a href="#CalendarServerbranchesuserscdabooreadonlydirectoryproxy2425twistedcaldavdirectorycalendaruserproxypy">CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/calendaruserproxy.py</a></li>
<li><a href="#CalendarServerbranchesuserscdabooreadonlydirectoryproxy2425twistedcaldavdirectorydirectorypy">CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/directory.py</a></li>
<li><a href="#CalendarServerbranchesuserscdabooreadonlydirectoryproxy2425twistedcaldavdirectoryprincipalpy">CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/principal.py</a></li>
<li><a href="#CalendarServerbranchesuserscdabooreadonlydirectoryproxy2425twistedcaldavdirectorytestaccountsxml">CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/test/accounts.xml</a></li>
<li><a href="#CalendarServerbranchesuserscdabooreadonlydirectoryproxy2425twistedcaldavdirectorytesttest_opendirectorypy">CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/test/test_opendirectory.py</a></li>
<li><a href="#CalendarServerbranchesuserscdabooreadonlydirectoryproxy2425twistedcaldavdirectorytesttest_opendirectoryschemapy">CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/test/test_opendirectoryschema.py</a></li>
<li><a href="#CalendarServerbranchesuserscdabooreadonlydirectoryproxy2425twistedcaldavdirectorytesttest_principalpy">CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/test/test_principal.py</a></li>
<li><a href="#CalendarServerbranchesuserscdabooreadonlydirectoryproxy2425twistedcaldavdirectoryxmlaccountsparserpy">CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/xmlaccountsparser.py</a></li>
<li><a href="#CalendarServerbranchesuserscdabooreadonlydirectoryproxy2425twistedcaldavdirectoryxmlfilepy">CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/xmlfile.py</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServerbranchesuserscdabooreadonlydirectoryproxy2425confaccountstestxml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/conf/accounts-test.xml (2426 => 2427)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/conf/accounts-test.xml        2008-05-18 01:07:42 UTC (rev 2426)
+++ CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/conf/accounts-test.xml        2008-05-19 01:28:17 UTC (rev 2427)
</span><span class="lines">@@ -55,6 +55,9 @@
</span><span class="cx">     &lt;proxies&gt;
</span><span class="cx">       &lt;member type=&quot;users&quot;&gt;user01&lt;/member&gt;
</span><span class="cx">     &lt;/proxies&gt;
</span><ins>+    &lt;read-only-proxies&gt;
+      &lt;member type=&quot;users&quot;&gt;user02&lt;/member&gt;
+    &lt;/read-only-proxies&gt;
</ins><span class="cx">   &lt;/resource&gt;
</span><span class="cx">   &lt;group&gt;
</span><span class="cx">     &lt;uid&gt;group01&lt;/uid&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdabooreadonlydirectoryproxy2425confaccountsdtd"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/conf/accounts.dtd (2426 => 2427)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/conf/accounts.dtd        2008-05-18 01:07:42 UTC (rev 2426)
+++ CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/conf/accounts.dtd        2008-05-19 01:28:17 UTC (rev 2427)
</span><span class="lines">@@ -25,22 +25,23 @@
</span><span class="cx">   &lt;!ELEMENT group (uid, guid, password, name, members, cuaddr*, disable-calendar?)&gt;
</span><span class="cx">     &lt;!ATTLIST group repeat CDATA &quot;1&quot;&gt;
</span><span class="cx"> 
</span><del>-  &lt;!ELEMENT resource (uid, guid, password, name, cuaddr*, auto-schedule?, proxies?)&gt;
</del><ins>+  &lt;!ELEMENT resource (uid, guid, password, name, cuaddr*, auto-schedule?, proxies?, read-only-proxies?)&gt;
</ins><span class="cx">     &lt;!ATTLIST resource repeat CDATA &quot;1&quot;&gt;
</span><span class="cx"> 
</span><del>-  &lt;!ELEMENT location (uid, guid, password, name, cuaddr*, auto-schedule?, proxies?)&gt;
</del><ins>+  &lt;!ELEMENT location (uid, guid, password, name, cuaddr*, auto-schedule?, proxies?, read-only-proxies?)&gt;
</ins><span class="cx">     &lt;!ATTLIST location repeat CDATA &quot;1&quot;&gt;
</span><span class="cx"> 
</span><span class="cx">   &lt;!ELEMENT member (#PCDATA)&gt;
</span><span class="cx">     &lt;!ATTLIST member type (users|groups|locations|resources) &quot;users&quot;&gt;
</span><span class="cx"> 
</span><del>-  &lt;!ELEMENT uid              (#PCDATA)&gt;
-  &lt;!ELEMENT guid             (#PCDATA)&gt;
-  &lt;!ELEMENT password         (#PCDATA)&gt;
-  &lt;!ELEMENT name             (#PCDATA)&gt;
-  &lt;!ELEMENT cuaddr           (#PCDATA)&gt;
-  &lt;!ELEMENT members          (member*)&gt;
-  &lt;!ELEMENT auto-schedule    EMPTY&gt;
-  &lt;!ELEMENT disable-calendar EMPTY&gt;
-  &lt;!ELEMENT proxies          (member*)&gt;
</del><ins>+  &lt;!ELEMENT uid               (#PCDATA)&gt;
+  &lt;!ELEMENT guid              (#PCDATA)&gt;
+  &lt;!ELEMENT password          (#PCDATA)&gt;
+  &lt;!ELEMENT name              (#PCDATA)&gt;
+  &lt;!ELEMENT cuaddr            (#PCDATA)&gt;
+  &lt;!ELEMENT members           (member*)&gt;
+  &lt;!ELEMENT auto-schedule     EMPTY&gt;
+  &lt;!ELEMENT disable-calendar  EMPTY&gt;
+  &lt;!ELEMENT proxies           (member*)&gt;
+  &lt;!ELEMENT read-only-proxies (member*)&gt;
</ins><span class="cx"> &gt;
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdabooreadonlydirectoryproxy2425twistedcaldavdirectoryappleopendirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/appleopendirectory.py (2426 => 2427)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/appleopendirectory.py        2008-05-18 01:07:42 UTC (rev 2426)
+++ CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/appleopendirectory.py        2008-05-19 01:28:17 UTC (rev 2427)
</span><span class="lines">@@ -345,13 +345,14 @@
</span><span class="cx">         @type guid: str
</span><span class="cx">         @param shortname: the record shortname of the record being parsed.
</span><span class="cx">         @type shortname: str
</span><del>-        @return: a C{tuple} of C{bool} for auto-accept and C{str} for proxy GUID.
</del><ins>+        @return: a C{tuple} of C{bool} for auto-accept, C{str} for proxy GUID, C{str} for read-only proxy GUID.
</ins><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         try:
</span><span class="cx">             plist = readPlistFromString(plist)
</span><span class="cx">             wpframework = plist.get(&quot;com.apple.WhitePagesFramework&quot;, {})
</span><span class="cx">             autoaccept = wpframework.get(&quot;AutoAcceptsInvitation&quot;, False)
</span><del>-            proxy = wpframework.get(&quot;CalendaringDelegate&quot;)
</del><ins>+            proxy = wpframework.get(&quot;CalendaringDelegate&quot;, None)
+            read_only_proxy = wpframework.get(&quot;ReadOnlyCalendaringDelegate&quot;, None)
</ins><span class="cx">         except AttributeError:
</span><span class="cx">             self.log_error(
</span><span class="cx">                 &quot;Failed to parse ResourceInfo attribute of record %s (%s): %s&quot; %
</span><span class="lines">@@ -359,8 +360,9 @@
</span><span class="cx">             )
</span><span class="cx">             autoaccept = False
</span><span class="cx">             proxy = None
</span><ins>+            read_only_proxy = None
</ins><span class="cx"> 
</span><del>-        return (autoaccept, proxy)
</del><ins>+        return (autoaccept, proxy, read_only_proxy,)
</ins><span class="cx"> 
</span><span class="cx">     def recordTypes(self):
</span><span class="cx">         return (
</span><span class="lines">@@ -542,12 +544,15 @@
</span><span class="cx">             # Special case for resources and locations
</span><span class="cx">             autoSchedule = False
</span><span class="cx">             proxyGUIDs = ()
</span><ins>+            readOnlyProxyGUIDs = ()
</ins><span class="cx">             if recordType in (DirectoryService.recordType_resources, DirectoryService.recordType_locations):
</span><span class="cx">                 resourceInfo = value.get(dsattributes.kDSNAttrResourceInfo)
</span><span class="cx">                 if resourceInfo is not None:
</span><del>-                    autoSchedule, proxy = self._parseResourceInfo(resourceInfo, recordGUID, recordShortName)
</del><ins>+                    autoSchedule, proxy, read_only_proxy = self._parseResourceInfo(resourceInfo, recordGUID, recordShortName)
</ins><span class="cx">                     if proxy:
</span><span class="cx">                         proxyGUIDs = (proxy,)
</span><ins>+                    if read_only_proxy:
+                        readOnlyProxyGUIDs = (read_only_proxy,)
</ins><span class="cx"> 
</span><span class="cx">             record = OpenDirectoryRecord(
</span><span class="cx">                 service               = self,
</span><span class="lines">@@ -561,6 +566,7 @@
</span><span class="cx">                 enabledForCalendaring = enabledForCalendaring,
</span><span class="cx">                 memberGUIDs           = memberGUIDs,
</span><span class="cx">                 proxyGUIDs            = proxyGUIDs,
</span><ins>+                readOnlyProxyGUIDs    = readOnlyProxyGUIDs,
</ins><span class="cx">             )
</span><span class="cx"> 
</span><span class="cx">             def disableRecord(record):
</span><span class="lines">@@ -772,7 +778,7 @@
</span><span class="cx">     def __init__(
</span><span class="cx">         self, service, recordType, guid, nodeName, shortName, fullName,
</span><span class="cx">         calendarUserAddresses, autoSchedule, enabledForCalendaring,
</span><del>-        memberGUIDs, proxyGUIDs,
</del><ins>+        memberGUIDs, proxyGUIDs, readOnlyProxyGUIDs,
</ins><span class="cx">     ):
</span><span class="cx">         super(OpenDirectoryRecord, self).__init__(
</span><span class="cx">             service               = service,
</span><span class="lines">@@ -787,6 +793,7 @@
</span><span class="cx">         self.nodeName = nodeName
</span><span class="cx">         self._memberGUIDs = tuple(memberGUIDs)
</span><span class="cx">         self._proxyGUIDs = tuple(proxyGUIDs)
</span><ins>+        self._readOnlyProxyGUIDs = tuple(readOnlyProxyGUIDs)
</ins><span class="cx"> 
</span><span class="cx">     def __repr__(self):
</span><span class="cx">         if self.service.realmName == self.nodeName:
</span><span class="lines">@@ -837,6 +844,25 @@
</span><span class="cx">             if self.guid in proxyRecord._proxyGUIDs:
</span><span class="cx">                 yield proxyRecord
</span><span class="cx"> 
</span><ins>+    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(&quot;No record for proxy in %s with GUID %s&quot; % (self.shortName, guid))
+            else:
+                yield proxyRecord
+
+    def readOnlyProxyFor(self):
+        for proxyRecord in itertools.chain(
+            self.service.recordsForType(DirectoryService.recordType_resources).itervalues(),
+            self.service.recordsForType(DirectoryService.recordType_locations).itervalues(),
+        ):
+            if self.guid in proxyRecord._readOnlyProxyGUIDs:
+                yield proxyRecord
+
</ins><span class="cx">     def verifyCredentials(self, credentials):
</span><span class="cx">         if isinstance(credentials, UsernamePassword):
</span><span class="cx">             # Check cached password
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdabooreadonlydirectoryproxy2425twistedcaldavdirectorycalendaruserproxypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/calendaruserproxy.py (2426 => 2427)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/calendaruserproxy.py        2008-05-18 01:07:42 UTC (rev 2426)
+++ CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/calendaruserproxy.py        2008-05-19 01:28:17 UTC (rev 2427)
</span><span class="lines">@@ -283,11 +283,11 @@
</span><span class="cx">             members = self._index().getMembers(self.uid)
</span><span class="cx">             return [p for p in [self.pcollection.principalForUID(uid) for uid in members] if p]
</span><span class="cx">         else:
</span><del>-            # Fixed proxies are only for read-write - the read-only list is empty
</del><ins>+            # Fixed proxies
</ins><span class="cx">             if self.proxyType == &quot;calendar-proxy-write&quot;:
</span><span class="cx">                 return self.parent.proxies()
</span><span class="cx">             else:
</span><del>-                return ()
</del><ins>+                return self.parent.readOnlyProxies()
</ins><span class="cx"> 
</span><span class="cx"> 
</span><span class="cx">     def groupMembers(self):
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdabooreadonlydirectoryproxy2425twistedcaldavdirectorydirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/directory.py (2426 => 2427)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/directory.py        2008-05-18 01:07:42 UTC (rev 2426)
+++ CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/directory.py        2008-05-19 01:28:17 UTC (rev 2427)
</span><span class="lines">@@ -206,6 +206,12 @@
</span><span class="cx">     def proxyFor(self):
</span><span class="cx">         return ()
</span><span class="cx"> 
</span><ins>+    def readOnlyProxies(self):
+        return ()
+
+    def readOnlyProxyFor(self):
+        return ()
+
</ins><span class="cx">     def hasEditableProxyMembership(self):
</span><span class="cx">         return self.recordType in (DirectoryService.recordType_users, DirectoryService.recordType_groups)
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdabooreadonlydirectoryproxy2425twistedcaldavdirectoryprincipalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/principal.py (2426 => 2427)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/principal.py        2008-05-18 01:07:42 UTC (rev 2426)
+++ CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/principal.py        2008-05-19 01:28:17 UTC (rev 2427)
</span><span class="lines">@@ -474,7 +474,7 @@
</span><span class="cx">     def principalURL(self):
</span><span class="cx">         return self._url
</span><span class="cx"> 
</span><del>-    def _getRelatives(self, method, record=None, relatives=None, records=None, proxy=False):
</del><ins>+    def _getRelatives(self, method, record=None, relatives=None, records=None, proxy=None):
</ins><span class="cx">         if record is None:
</span><span class="cx">             record = self.record
</span><span class="cx">         if relatives is None:
</span><span class="lines">@@ -492,7 +492,10 @@
</span><span class="cx">                         log.err(&quot;No principal found for directory record: %r&quot; % (relative,))
</span><span class="cx">                     else:
</span><span class="cx">                         if proxy:
</span><del>-                            found = found.getChild(&quot;calendar-proxy-write&quot;)
</del><ins>+                            if proxy == &quot;read-write&quot;:
+                                found = found.getChild(&quot;calendar-proxy-write&quot;)
+                            else:
+                                found = found.getChild(&quot;calendar-proxy-read&quot;)
</ins><span class="cx">                         relatives.add(found)
</span><span class="cx"> 
</span><span class="cx">                     self._getRelatives(method, relative, relatives, records)
</span><span class="lines">@@ -507,7 +510,8 @@
</span><span class="cx"> 
</span><span class="cx">         if config.EnableProxyPrincipals:
</span><span class="cx">             # Get any directory specified proxies
</span><del>-            groups.update(self._getRelatives(&quot;proxyFor&quot;, proxy=True))
</del><ins>+            groups.update(self._getRelatives(&quot;proxyFor&quot;, proxy='read-write'))
+            groups.update(self._getRelatives(&quot;readOnlyProxyFor&quot;, proxy='read-only'))
</ins><span class="cx"> 
</span><span class="cx">             # Get proxy group UIDs and map to principal resources
</span><span class="cx">             proxies = []
</span><span class="lines">@@ -610,6 +614,9 @@
</span><span class="cx">     def proxies(self):
</span><span class="cx">         return self._getRelatives(&quot;proxies&quot;)
</span><span class="cx"> 
</span><ins>+    def readOnlyProxies(self):
+        return self._getRelatives(&quot;readOnlyProxies&quot;)
+
</ins><span class="cx">     def hasEditableProxyMembership(self):
</span><span class="cx">         return self.record.hasEditableProxyMembership()
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdabooreadonlydirectoryproxy2425twistedcaldavdirectorytestaccountsxml"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/test/accounts.xml (2426 => 2427)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/test/accounts.xml        2008-05-18 01:07:42 UTC (rev 2426)
+++ CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/test/accounts.xml        2008-05-19 01:28:17 UTC (rev 2427)
</span><span class="lines">@@ -197,5 +197,8 @@
</span><span class="cx">     &lt;proxies&gt;
</span><span class="cx">       &lt;member type=&quot;groups&quot;&gt;non_calendar_group&lt;/member&gt;
</span><span class="cx">     &lt;/proxies&gt;
</span><ins>+    &lt;read-only-proxies&gt;
+      &lt;member type=&quot;groups&quot;&gt;recursive2_coasts&lt;/member&gt;
+    &lt;/read-only-proxies&gt;
</ins><span class="cx">   &lt;/resource&gt;
</span><span class="cx"> &lt;/accounts&gt;
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdabooreadonlydirectoryproxy2425twistedcaldavdirectorytesttest_opendirectorypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/test/test_opendirectory.py (2426 => 2427)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/test/test_opendirectory.py        2008-05-18 01:07:42 UTC (rev 2426)
+++ CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/test/test_opendirectory.py        2008-05-19 01:28:17 UTC (rev 2427)
</span><span class="lines">@@ -72,6 +72,7 @@
</span><span class="cx">                 enabledForCalendaring = True,
</span><span class="cx">                 memberGUIDs           = [],
</span><span class="cx">                 proxyGUIDs            = (),
</span><ins>+                readOnlyProxyGUIDs    = (),
</ins><span class="cx">             )
</span><span class="cx"> 
</span><span class="cx">             digestFields = {}
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdabooreadonlydirectoryproxy2425twistedcaldavdirectorytesttest_opendirectoryschemapy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/test/test_opendirectoryschema.py (2426 => 2427)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/test/test_opendirectoryschema.py        2008-05-18 01:07:42 UTC (rev 2426)
+++ CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/test/test_opendirectoryschema.py        2008-05-19 01:28:17 UTC (rev 2427)
</span><span class="lines">@@ -1186,6 +1186,8 @@
</span><span class="cx">         &lt;string&gt;Location&lt;/string&gt;
</span><span class="cx">         &lt;key&gt;CalendaringDelegate&lt;/key&gt;
</span><span class="cx">         &lt;string&gt;1234-GUID-5678&lt;/string&gt;
</span><ins>+        &lt;key&gt;ReadOnlyCalendaringDelegate&lt;/key&gt;
+        &lt;string&gt;1234-GUID-5679&lt;/string&gt;
</ins><span class="cx">     &lt;/dict&gt;
</span><span class="cx"> &lt;/dict&gt;
</span><span class="cx"> &lt;/plist&gt;
</span><span class="lines">@@ -1203,6 +1205,8 @@
</span><span class="cx">         &lt;string&gt;Location&lt;/string&gt;
</span><span class="cx">         &lt;key&gt;CalendaringDelegate&lt;/key&gt;
</span><span class="cx">         &lt;string&gt;&lt;/string&gt;
</span><ins>+        &lt;key&gt;ReadOnlyCalendaringDelegate&lt;/key&gt;
+        &lt;string&gt;&lt;/string&gt;
</ins><span class="cx">     &lt;/dict&gt;
</span><span class="cx"> &lt;/dict&gt;
</span><span class="cx"> &lt;/plist&gt;
</span><span class="lines">@@ -1243,6 +1247,8 @@
</span><span class="cx">         &lt;string&gt;Location&lt;/string&gt;
</span><span class="cx">         &lt;key&gt;CalendaringDelegate&lt;/key&gt;
</span><span class="cx">         &lt;string&gt;1234-GUID-5678&lt;/string&gt;
</span><ins>+        &lt;key&gt;ReadOnlyCalendaringDelegate&lt;/key&gt;
+        &lt;string&gt;1234-GUID-5679&lt;/string&gt;
</ins><span class="cx">     &lt;/dict&gt;
</span><span class="cx"> &lt;/dict&gt;
</span><span class="cx"> &lt;/plist&gt;
</span><span class="lines">@@ -1260,18 +1266,19 @@
</span><span class="cx"> &quot;&quot;&quot;
</span><span class="cx"> 
</span><span class="cx">         test_bool = (
</span><del>-            (plist_good_false, False, &quot;1234-GUID-5678&quot;),
-            (plist_good_true, True, &quot;&quot;),
-            (plist_good_missing, False, None),
-            (plist_wrong, False, None),
-            (plist_bad, False, None),
-            (plist_invalid, False, None),
</del><ins>+            (plist_good_false, False, &quot;1234-GUID-5678&quot;, &quot;1234-GUID-5679&quot;),
+            (plist_good_true, True, &quot;&quot;, &quot;&quot;),
+            (plist_good_missing, False, None, None),
+            (plist_wrong, False, None, None),
+            (plist_bad, False, None, None),
+            (plist_invalid, False, None, None),
</ins><span class="cx">         )
</span><span class="cx"> 
</span><span class="cx">         def test_plists(self):
</span><span class="cx">             service = OpenDirectoryService(node=&quot;/Search&quot;, dosetup=False)
</span><span class="cx">             
</span><span class="cx">             for item in ODResourceInfoParse.test_bool:
</span><del>-                item1, item2 = service._parseResourceInfo(item[0], &quot;guid&quot;, &quot;name&quot;)
</del><ins>+                item1, item2, item3 = service._parseResourceInfo(item[0], &quot;guid&quot;, &quot;name&quot;)
</ins><span class="cx">                 self.assertEqual(item1, item[1])
</span><span class="cx">                 self.assertEqual(item2, item[2])
</span><ins>+                self.assertEqual(item3, item[3])
</ins></span></pre></div>
<a id="CalendarServerbranchesuserscdabooreadonlydirectoryproxy2425twistedcaldavdirectorytesttest_principalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/test/test_principal.py (2426 => 2427)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/test/test_principal.py        2008-05-18 01:07:42 UTC (rev 2426)
+++ CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/test/test_principal.py        2008-05-19 01:28:17 UTC (rev 2427)
</span><span class="lines">@@ -267,6 +267,15 @@
</span><span class="cx">                 self.failUnless(set(record.proxies()).issubset(set(r.record for r in recordResource.proxies())))
</span><span class="cx">                 self.assertEqual(record.hasEditableProxyMembership(), recordResource.hasEditableProxyMembership())
</span><span class="cx"> 
</span><ins>+    def test_read_only_proxies(self):
+        &quot;&quot;&quot;
+        DirectoryPrincipalResource.proxies()
+        &quot;&quot;&quot;
+        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())
+
</ins><span class="cx">     def test_principalUID(self):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="cx">         DirectoryPrincipalResource.principalUID()
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdabooreadonlydirectoryproxy2425twistedcaldavdirectoryxmlaccountsparserpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/xmlaccountsparser.py (2426 => 2427)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/xmlaccountsparser.py        2008-05-18 01:07:42 UTC (rev 2426)
+++ CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/xmlaccountsparser.py        2008-05-19 01:28:17 UTC (rev 2427)
</span><span class="lines">@@ -29,26 +29,27 @@
</span><span class="cx"> 
</span><span class="cx"> from twistedcaldav.directory.directory import DirectoryService
</span><span class="cx"> 
</span><del>-ELEMENT_ACCOUNTS        = &quot;accounts&quot;
-ELEMENT_USER            = &quot;user&quot;
-ELEMENT_GROUP           = &quot;group&quot;
-ELEMENT_LOCATION        = &quot;location&quot;
-ELEMENT_RESOURCE        = &quot;resource&quot;
</del><ins>+ELEMENT_ACCOUNTS          = &quot;accounts&quot;
+ELEMENT_USER              = &quot;user&quot;
+ELEMENT_GROUP             = &quot;group&quot;
+ELEMENT_LOCATION          = &quot;location&quot;
+ELEMENT_RESOURCE          = &quot;resource&quot;
</ins><span class="cx"> 
</span><del>-ELEMENT_SHORTNAME       = &quot;uid&quot;
-ELEMENT_GUID            = &quot;guid&quot;
-ELEMENT_PASSWORD        = &quot;password&quot;
-ELEMENT_NAME            = &quot;name&quot;
-ELEMENT_MEMBERS         = &quot;members&quot;
-ELEMENT_MEMBER          = &quot;member&quot;
-ELEMENT_CUADDR          = &quot;cuaddr&quot;
-ELEMENT_AUTOSCHEDULE    = &quot;auto-schedule&quot;
-ELEMENT_DISABLECALENDAR = &quot;disable-calendar&quot;
-ELEMENT_PROXIES         = &quot;proxies&quot;
</del><ins>+ELEMENT_SHORTNAME         = &quot;uid&quot;
+ELEMENT_GUID              = &quot;guid&quot;
+ELEMENT_PASSWORD          = &quot;password&quot;
+ELEMENT_NAME              = &quot;name&quot;
+ELEMENT_MEMBERS           = &quot;members&quot;
+ELEMENT_MEMBER            = &quot;member&quot;
+ELEMENT_CUADDR            = &quot;cuaddr&quot;
+ELEMENT_AUTOSCHEDULE      = &quot;auto-schedule&quot;
+ELEMENT_DISABLECALENDAR   = &quot;disable-calendar&quot;
+ELEMENT_PROXIES           = &quot;proxies&quot;
+ELEMENT_READ_ONLY_PROXIES = &quot;read-only-proxies&quot;
</ins><span class="cx"> 
</span><del>-ATTRIBUTE_REALM         = &quot;realm&quot;
-ATTRIBUTE_REPEAT        = &quot;repeat&quot;
-ATTRIBUTE_RECORDTYPE    = &quot;type&quot;
</del><ins>+ATTRIBUTE_REALM           = &quot;realm&quot;
+ATTRIBUTE_REPEAT          = &quot;repeat&quot;
+ATTRIBUTE_RECORDTYPE      = &quot;type&quot;
</ins><span class="cx"> 
</span><span class="cx"> RECORD_TYPES = {
</span><span class="cx">     ELEMENT_USER     : DirectoryService.recordType_users,
</span><span class="lines">@@ -109,6 +110,12 @@
</span><span class="cx">                 if item is not None:
</span><span class="cx">                     item.proxyFor.add((proxier.recordType, proxier.shortName))
</span><span class="cx"> 
</span><ins>+            # 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))
+
</ins><span class="cx">         for child in node._get_childNodes():
</span><span class="cx">             child_name = child._get_localName()
</span><span class="cx">             if child_name is None:
</span><span class="lines">@@ -159,6 +166,8 @@
</span><span class="cx">         self.enabledForCalendaring = True
</span><span class="cx">         self.proxies = set()
</span><span class="cx">         self.proxyFor = set()
</span><ins>+        self.readOnlyProxies = set()
+        self.readOnlyProxyFor = set()
</ins><span class="cx"> 
</span><span class="cx">     def repeat(self, ctr):
</span><span class="cx">         &quot;&quot;&quot;
</span><span class="lines">@@ -199,6 +208,7 @@
</span><span class="cx">         result.autoSchedule = self.autoSchedule
</span><span class="cx">         result.enabledForCalendaring = self.enabledForCalendaring
</span><span class="cx">         result.proxies = self.proxies
</span><ins>+        result.readOnlyProxies = self.readOnlyProxies
</ins><span class="cx">         return result
</span><span class="cx"> 
</span><span class="cx">     def parseXML(self, node):
</span><span class="lines">@@ -238,6 +248,11 @@
</span><span class="cx">                 if self.recordType not in (DirectoryService.recordType_resources, DirectoryService.recordType_locations):
</span><span class="cx">                     raise ValueError(&quot;&lt;auto-schedule&gt; element only allowed for Resources and Locations: %s&quot; % (child_name,))
</span><span class="cx">                 self._parseMembers(child, self.proxies)
</span><ins>+            elif child_name == ELEMENT_READ_ONLY_PROXIES:
+                # Only Resources &amp; Locations
+                if self.recordType not in (DirectoryService.recordType_resources, DirectoryService.recordType_locations):
+                    raise ValueError(&quot;&lt;auto-schedule&gt; element only allowed for Resources and Locations: %s&quot; % (child_name,))
+                self._parseMembers(child, self.readOnlyProxies)
</ins><span class="cx">             else:
</span><span class="cx">                 raise RuntimeError(&quot;Unknown account attribute: %s&quot; % (child_name,))
</span><span class="cx"> 
</span></span></pre></div>
<a id="CalendarServerbranchesuserscdabooreadonlydirectoryproxy2425twistedcaldavdirectoryxmlfilepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/xmlfile.py (2426 => 2427)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/xmlfile.py        2008-05-18 01:07:42 UTC (rev 2426)
+++ CalendarServer/branches/users/cdaboo/read-only-directory-proxy-2425/twistedcaldav/directory/xmlfile.py        2008-05-19 01:28:17 UTC (rev 2427)
</span><span class="lines">@@ -113,11 +113,13 @@
</span><span class="cx">             enabledForCalendaring = xmlPrincipal.enabledForCalendaring,
</span><span class="cx">         )
</span><span class="cx"> 
</span><del>-        self.password     = xmlPrincipal.password
-        self._members     = xmlPrincipal.members
-        self._groups      = xmlPrincipal.groups
-        self._proxies     = xmlPrincipal.proxies
-        self._proxyFor    = xmlPrincipal.proxyFor
</del><ins>+        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
</ins><span class="cx"> 
</span><span class="cx">     def members(self):
</span><span class="cx">         for recordType, shortName in self._members:
</span><span class="lines">@@ -131,10 +133,18 @@
</span><span class="cx">         for recordType, shortName in self._proxies:
</span><span class="cx">             yield self.service.recordWithShortName(recordType, shortName)
</span><span class="cx"> 
</span><del>-    def proxyFor(self):
</del><ins>+    def proxyFor(self, read_write=True):
</ins><span class="cx">         for recordType, shortName in self._proxyFor:
</span><span class="cx">             yield self.service.recordWithShortName(recordType, shortName)
</span><span class="cx"> 
</span><ins>+    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)
+
</ins><span class="cx">     def verifyCredentials(self, credentials):
</span><span class="cx">         if isinstance(credentials, UsernamePassword):
</span><span class="cx">             return credentials.password == self.password
</span></span></pre>
</div>
</div>

</body>
</html>