<!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" />
<title>[13620] CalendarServer/trunk</title>
</head>
<body>
<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { 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 { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#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>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.calendarserver.org//changeset/13620">13620</a></dd>
<dt>Author</dt> <dd>cdaboo@apple.com</dd>
<dt>Date</dt> <dd>2014-06-09 09:25:14 -0700 (Mon, 09 Jun 2014)</dd>
</dl>
<h3>Log Message</h3>
<pre>Add the ability to have per-proxy properties for certain specific (configurable) properties (e.g., calendar color). Make sure read-only
proxies can write properties. This involved changing the way HTTP authentication is handled to make sure it is always done on the root
resource during request-URI traversal so that the authz_uid is known before any store transaction is created, because the txn needs
to know that to properly setup property stores so the right "view" of per-user properties is used.</pre>
<h3>Modified Paths</h3>
<ul>
<li><a href="#CalendarServertrunkcalendarserverprovisionrootpy">CalendarServer/trunk/calendarserver/provision/root.py</a></li>
<li><a href="#CalendarServertrunkcalendarservertapcaldavpy">CalendarServer/trunk/calendarserver/tap/caldav.py</a></li>
<li><a href="#CalendarServertrunkcalendarservertoolscmdlinepy">CalendarServer/trunk/calendarserver/tools/cmdline.py</a></li>
<li><a href="#CalendarServertrunkcalendarservertoolstesttest_gatewaypy">CalendarServer/trunk/calendarserver/tools/test/test_gateway.py</a></li>
<li><a href="#CalendarServertrunkcalendarservertoolstesttest_principalspy">CalendarServer/trunk/calendarserver/tools/test/test_principals.py</a></li>
<li><a href="#CalendarServertrunktwistedcaldavdirectoryutilpy">CalendarServer/trunk/twistedcaldav/directory/util.py</a></li>
<li><a href="#CalendarServertrunktwistedcaldavresourcepy">CalendarServer/trunk/twistedcaldav/resource.py</a></li>
<li><a href="#CalendarServertrunktwistedcaldavscheduling_storecaldavresourcepy">CalendarServer/trunk/twistedcaldav/scheduling_store/caldav/resource.py</a></li>
<li><a href="#CalendarServertrunktwistedcaldavsharingpy">CalendarServer/trunk/twistedcaldav/sharing.py</a></li>
<li><a href="#CalendarServertrunktwistedcaldavstdconfigpy">CalendarServer/trunk/twistedcaldav/stdconfig.py</a></li>
<li><a href="#CalendarServertrunktwistedcaldavstorebridgepy">CalendarServer/trunk/twistedcaldav/storebridge.py</a></li>
<li><a href="#CalendarServertrunktxdavbasepropertystorebasepy">CalendarServer/trunk/txdav/base/propertystore/base.py</a></li>
<li><a href="#CalendarServertrunktxdavbasepropertystoresqlpy">CalendarServer/trunk/txdav/base/propertystore/sql.py</a></li>
<li><a href="#CalendarServertrunktxdavbasepropertystoretestbasepy">CalendarServer/trunk/txdav/base/propertystore/test/base.py</a></li>
<li><a href="#CalendarServertrunktxdavbasepropertystoretesttest_sqlpy">CalendarServer/trunk/txdav/base/propertystore/test/test_sql.py</a></li>
<li><a href="#CalendarServertrunktxdavbasepropertystoretesttest_xattrpy">CalendarServer/trunk/txdav/base/propertystore/test/test_xattr.py</a></li>
<li><a href="#CalendarServertrunktxdavcaldavdatastorefilepy">CalendarServer/trunk/txdav/caldav/datastore/file.py</a></li>
<li><a href="#CalendarServertrunktxdavcaldavdatastoresqlpy">CalendarServer/trunk/txdav/caldav/datastore/sql.py</a></li>
<li><a href="#CalendarServertrunktxdavcaldavdatastoretesttest_implicitpy">CalendarServer/trunk/txdav/caldav/datastore/test/test_implicit.py</a></li>
<li><a href="#CalendarServertrunktxdavcaldavdatastoretesttest_sql_sharingpy">CalendarServer/trunk/txdav/caldav/datastore/test/test_sql_sharing.py</a></li>
<li><a href="#CalendarServertrunktxdavcarddavdatastorefilepy">CalendarServer/trunk/txdav/carddav/datastore/file.py</a></li>
<li><a href="#CalendarServertrunktxdavcarddavdatastoresqlpy">CalendarServer/trunk/txdav/carddav/datastore/sql.py</a></li>
<li><a href="#CalendarServertrunktxdavcommondatastorefilepy">CalendarServer/trunk/txdav/common/datastore/file.py</a></li>
<li><a href="#CalendarServertrunktxdavcommondatastoresqlpy">CalendarServer/trunk/txdav/common/datastore/sql.py</a></li>
<li><a href="#CalendarServertrunktxdavcommondatastoresql_externalpy">CalendarServer/trunk/txdav/common/datastore/sql_external.py</a></li>
</ul>
</div>
<div id="patch">
<h3>Diff</h3>
<a id="CalendarServertrunkcalendarserverprovisionrootpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/calendarserver/provision/root.py (13619 => 13620)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/calendarserver/provision/root.py        2014-06-09 16:04:50 UTC (rev 13619)
+++ CalendarServer/trunk/calendarserver/provision/root.py        2014-06-09 16:25:14 UTC (rev 13620)
</span><span class="lines">@@ -364,12 +364,7 @@
</span><span class="cx"> .format(agent)
</span><span class="cx"> ))
</span><span class="cx">
</span><del>- if (
- config.EnableResponseCache and
- request.method == "PROPFIND" and
- not getattr(request, "notInCache", False) and
- len(segments) > 1
- ):
</del><ins>+ if not hasattr(request, "authzUser"):
</ins><span class="cx"> try:
</span><span class="cx"> authnUser, authzUser = yield self.authenticate(request)
</span><span class="cx"> request.authnUser = authnUser
</span><span class="lines">@@ -381,6 +376,13 @@
</span><span class="cx"> )
</span><span class="cx"> raise HTTPError(response)
</span><span class="cx">
</span><ins>+ if (
+ config.EnableResponseCache and
+ request.method == "PROPFIND" and
+ not getattr(request, "notInCache", False) and
+ len(segments) > 1
+ ):
+
</ins><span class="cx"> try:
</span><span class="cx"> if not getattr(request, "checkingCache", False):
</span><span class="cx"> request.checkingCache = True
</span></span></pre></div>
<a id="CalendarServertrunkcalendarservertapcaldavpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/calendarserver/tap/caldav.py (13619 => 13620)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/calendarserver/tap/caldav.py        2014-06-09 16:04:50 UTC (rev 13619)
+++ CalendarServer/trunk/calendarserver/tap/caldav.py        2014-06-09 16:25:14 UTC (rev 13620)
</span><span class="lines">@@ -38,6 +38,8 @@
</span><span class="cx">
</span><span class="cx"> from zope.interface import implements
</span><span class="cx">
</span><ins>+from twistedcaldav.stdconfig import config
+
</ins><span class="cx"> from twisted.python.log import FileLogObserver, ILogObserver
</span><span class="cx"> from twisted.python.logfile import LogFile
</span><span class="cx"> from twisted.python.usage import Options, UsageError
</span><span class="lines">@@ -96,7 +98,7 @@
</span><span class="cx"> from txdav.who.groups import GroupCacher
</span><span class="cx">
</span><span class="cx"> from twistedcaldav import memcachepool
</span><del>-from twistedcaldav.config import config, ConfigurationError
</del><ins>+from twistedcaldav.config import ConfigurationError
</ins><span class="cx"> from twistedcaldav.localization import processLocalizationFiles
</span><span class="cx"> from twistedcaldav.stdconfig import DEFAULT_CONFIG, DEFAULT_CONFIG_FILE
</span><span class="cx"> from twistedcaldav.upgrade import (
</span></span></pre></div>
<a id="CalendarServertrunkcalendarservertoolscmdlinepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/calendarserver/tools/cmdline.py (13619 => 13620)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/calendarserver/tools/cmdline.py        2014-06-09 16:04:50 UTC (rev 13619)
+++ CalendarServer/trunk/calendarserver/tools/cmdline.py        2014-06-09 16:25:14 UTC (rev 13620)
</span><span class="lines">@@ -18,6 +18,7 @@
</span><span class="cx"> Shared main-point between utilities.
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+from twistedcaldav.stdconfig import config
</ins><span class="cx"> from calendarserver.tap.caldav import CalDAVServiceMaker, CalDAVOptions
</span><span class="cx"> from calendarserver.tap.util import checkDirectories
</span><span class="cx"> from calendarserver.tools.util import loadConfig, autoDisableMemcached
</span></span></pre></div>
<a id="CalendarServertrunkcalendarservertoolstesttest_gatewaypy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/calendarserver/tools/test/test_gateway.py (13619 => 13620)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/calendarserver/tools/test/test_gateway.py        2014-06-09 16:04:50 UTC (rev 13619)
+++ CalendarServer/trunk/calendarserver/tools/test/test_gateway.py        2014-06-09 16:25:14 UTC (rev 13620)
</span><span class="lines">@@ -20,12 +20,12 @@
</span><span class="cx"> import plistlib
</span><span class="cx"> import xml
</span><span class="cx">
</span><ins>+from twistedcaldav.stdconfig import config
</ins><span class="cx"> from twext.python.filepath import CachingFilePath as FilePath
</span><span class="cx"> from twisted.internet import reactor
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks, Deferred, returnValue
</span><span class="cx"> from twisted.trial.unittest import TestCase
</span><span class="cx"> from twistedcaldav import memcacher
</span><del>-from twistedcaldav.config import config
</del><span class="cx"> from twistedcaldav.memcacheclient import ClientFactory
</span><span class="cx"> from twistedcaldav.test.util import CapturingProcessProtocol
</span><span class="cx"> from txdav.common.datastore.test.util import (
</span></span></pre></div>
<a id="CalendarServertrunkcalendarservertoolstesttest_principalspy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/calendarserver/tools/test/test_principals.py (13619 => 13620)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/calendarserver/tools/test/test_principals.py        2014-06-09 16:04:50 UTC (rev 13619)
+++ CalendarServer/trunk/calendarserver/tools/test/test_principals.py        2014-06-09 16:25:14 UTC (rev 13620)
</span><span class="lines">@@ -16,6 +16,7 @@
</span><span class="cx">
</span><span class="cx"> import os
</span><span class="cx">
</span><ins>+from twistedcaldav.stdconfig import config
</ins><span class="cx"> from calendarserver.tools.principals import (
</span><span class="cx"> parseCreationArgs, matchStrings,
</span><span class="cx"> recordForPrincipalID, getProxies, setProxies
</span><span class="lines">@@ -23,7 +24,6 @@
</span><span class="cx"> from twext.python.filepath import CachingFilePath as FilePath
</span><span class="cx"> from twisted.internet import reactor
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks, Deferred, returnValue
</span><del>-from twistedcaldav.config import config
</del><span class="cx"> from twistedcaldav.test.util import (
</span><span class="cx"> TestCase, StoreTestCase, CapturingProcessProtocol, ErrorOutput
</span><span class="cx"> )
</span></span></pre></div>
<a id="CalendarServertrunktwistedcaldavdirectoryutilpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/twistedcaldav/directory/util.py (13619 => 13620)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/twistedcaldav/directory/util.py        2014-06-09 16:04:50 UTC (rev 13619)
+++ CalendarServer/trunk/twistedcaldav/directory/util.py        2014-06-09 16:25:14 UTC (rev 13620)
</span><span class="lines">@@ -92,7 +92,11 @@
</span><span class="cx"> """
</span><span class="cx"> transaction = getattr(request, TRANSACTION_KEY, None)
</span><span class="cx"> if transaction is None:
</span><del>- transaction = newStore.newTransaction(repr(request))
</del><ins>+ if hasattr(request, "authzUser") and request.authzUser is not None:
+ authz_uid = request.authzUser.record.uid
+ else:
+ authz_uid = None
+ transaction = newStore.newTransaction(repr(request), authz_uid=authz_uid)
</ins><span class="cx"> def abortIfUncommitted(request, response):
</span><span class="cx"> try:
</span><span class="cx"> # TODO: missing 'yield' here. For formal correctness as per
</span></span></pre></div>
<a id="CalendarServertrunktwistedcaldavresourcepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/twistedcaldav/resource.py (13619 => 13620)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/twistedcaldav/resource.py        2014-06-09 16:04:50 UTC (rev 13619)
+++ CalendarServer/trunk/twistedcaldav/resource.py        2014-06-09 16:25:14 UTC (rev 13620)
</span><span class="lines">@@ -2635,11 +2635,16 @@
</span><span class="cx"> if config.EnableProxyPrincipals:
</span><span class="cx"> # Server may be read only
</span><span class="cx"> if config.EnableReadOnlyServer:
</span><del>- rw_proxy_privs = (
</del><ins>+ ro_proxy_privs = rw_proxy_privs = (
</ins><span class="cx"> element.Privilege(element.Read()),
</span><span class="cx"> element.Privilege(element.ReadCurrentUserPrivilegeSet()),
</span><span class="cx"> )
</span><span class="cx"> else:
</span><ins>+ ro_proxy_privs = (
+ element.Privilege(element.Read()),
+ element.Privilege(element.ReadCurrentUserPrivilegeSet()),
+ element.Privilege(element.WriteProperties()),
+ )
</ins><span class="cx"> rw_proxy_privs = (
</span><span class="cx"> element.Privilege(element.Read()),
</span><span class="cx"> element.Privilege(element.ReadCurrentUserPrivilegeSet()),
</span><span class="lines">@@ -2650,10 +2655,7 @@
</span><span class="cx"> # DAV:read/DAV:read-current-user-privilege-set access for this principal's calendar-proxy-read users.
</span><span class="cx"> element.ACE(
</span><span class="cx"> element.Principal(element.HRef(joinURL(myPrincipal.principalURL(), "calendar-proxy-read/"))),
</span><del>- element.Grant(
- element.Privilege(element.Read()),
- element.Privilege(element.ReadCurrentUserPrivilegeSet()),
- ),
</del><ins>+ element.Grant(*ro_proxy_privs),
</ins><span class="cx"> element.Protected(),
</span><span class="cx"> TwistedACLInheritable(),
</span><span class="cx"> ),
</span></span></pre></div>
<a id="CalendarServertrunktwistedcaldavscheduling_storecaldavresourcepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/twistedcaldav/scheduling_store/caldav/resource.py (13619 => 13620)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/twistedcaldav/scheduling_store/caldav/resource.py        2014-06-09 16:04:50 UTC (rev 13619)
+++ CalendarServer/trunk/twistedcaldav/scheduling_store/caldav/resource.py        2014-06-09 16:25:14 UTC (rev 13620)
</span><span class="lines">@@ -419,17 +419,6 @@
</span><span class="cx"> originator = (yield self.loadOriginatorFromRequestDetails(request))
</span><span class="cx"> recipients = self.loadRecipientsFromCalendarData(calendar)
</span><span class="cx">
</span><del>- # storeComponent needs to know who the auth'd user is for access control
- # TODO: this needs to be done in a better way - ideally when the txn is created for the request,
- # we should set a txn.authzid attribute.
- authz = None
- authz_principal = self.parent.currentPrincipal(request).children[0]
- if isinstance(authz_principal, davxml.HRef):
- principalURL = str(authz_principal)
- if principalURL:
- authz = (yield request.locateResource(principalURL))
- self._associatedTransaction._authz_uid = authz.record.uid
-
</del><span class="cx"> # Log extended item
</span><span class="cx"> if not hasattr(request, "extendedLogItems"):
</span><span class="cx"> request.extendedLogItems = {}
</span></span></pre></div>
<a id="CalendarServertrunktwistedcaldavsharingpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/twistedcaldav/sharing.py (13619 => 13620)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/twistedcaldav/sharing.py        2014-06-09 16:04:50 UTC (rev 13619)
+++ CalendarServer/trunk/twistedcaldav/sharing.py        2014-06-09 16:25:14 UTC (rev 13620)
</span><span class="lines">@@ -383,6 +383,7 @@
</span><span class="cx"> element.Grant(
</span><span class="cx"> element.Privilege(element.Read()),
</span><span class="cx"> element.Privilege(element.ReadCurrentUserPrivilegeSet()),
</span><ins>+ element.Privilege(element.WriteProperties()),
</ins><span class="cx"> ),
</span><span class="cx"> element.Protected(),
</span><span class="cx"> TwistedACLInheritable(),
</span></span></pre></div>
<a id="CalendarServertrunktwistedcaldavstdconfigpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/twistedcaldav/stdconfig.py (13619 => 13620)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/twistedcaldav/stdconfig.py        2014-06-09 16:04:50 UTC (rev 13619)
+++ CalendarServer/trunk/twistedcaldav/stdconfig.py        2014-06-09 16:25:14 UTC (rev 13620)
</span><span class="lines">@@ -606,9 +606,31 @@
</span><span class="cx"> "IgnorePerUserProperties" : [
</span><span class="cx"> "X-APPLE-STRUCTURED-LOCATION",
</span><span class="cx"> ],
</span><ins>+ "CollectionProperties": {
+ "Shadowable": [
+ "{urn:ietf:params:xml:ns:caldav}calendar-description",
+ ],
+ "ProxyOverride": [
+ "{urn:ietf:params:xml:ns:caldav}calendar-description",
+ "{com.apple.ical:}calendarcolor",
+ "{http://apple.com/ns/ical/}calendar-color",
+ "{http://apple.com/ns/ical/}calendar-order",
+ ],
+ "Global": [
+ ],
+ },
</ins><span class="cx"> },
</span><span class="cx"> "AddressBooks" : {
</span><span class="cx"> "Enabled" : False, # Address Books on/off switch
</span><ins>+ "CollectionProperties": {
+ "Shadowable": [
+ "{urn:ietf:params:xml:ns:carddav}addressbook-description",
+ ],
+ "ProxyOverride": [
+ ],
+ "Global": [
+ ],
+ },
</ins><span class="cx"> },
</span><span class="cx"> },
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServertrunktwistedcaldavstorebridgepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/twistedcaldav/storebridge.py (13619 => 13620)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/twistedcaldav/storebridge.py        2014-06-09 16:04:50 UTC (rev 13619)
+++ CalendarServer/trunk/twistedcaldav/storebridge.py        2014-06-09 16:25:14 UTC (rev 13620)
</span><span class="lines">@@ -2882,17 +2882,6 @@
</span><span class="cx"> "Can't parse calendar data: %s" % (str(e),)
</span><span class="cx"> ))
</span><span class="cx">
</span><del>- # storeComponent needs to know who the auth'd user is for access control
- # TODO: this needs to be done in a better way - ideally when the txn is created for the request,
- # we should set a txn.authzid attribute.
- authz = None
- authz_principal = self._parentResource.currentPrincipal(request).children[0]
- if isinstance(authz_principal, davxml.HRef):
- principalURL = str(authz_principal)
- if principalURL:
- authz = (yield request.locateResource(principalURL))
- self._parentResource._newStoreObject._txn._authz_uid = authz.record.uid
-
</del><span class="cx"> try:
</span><span class="cx"> response = (yield self.storeComponent(component, smart_merge=schedule_tag_match))
</span><span class="cx"> except ResourceDeletedError:
</span><span class="lines">@@ -3623,17 +3612,6 @@
</span><span class="cx"> "Could not parse vCard",
</span><span class="cx"> ))
</span><span class="cx">
</span><del>- # storeComponent needs to know who the auth'd user is for access control
- # TODO: this needs to be done in a better way - ideally when the txn is created for the request,
- # we should set a txn.authzid attribute.
- authz = None
- authz_principal = self._parentResource.currentPrincipal(request).children[0]
- if isinstance(authz_principal, davxml.HRef):
- principalURL = str(authz_principal)
- if principalURL:
- authz = (yield request.locateResource(principalURL))
- self._parentResource._newStoreObject._txn._authz_uid = authz.record.uid
-
</del><span class="cx"> try:
</span><span class="cx"> response = (yield self.storeComponent(component))
</span><span class="cx"> except ResourceDeletedError:
</span></span></pre></div>
<a id="CalendarServertrunktxdavbasepropertystorebasepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/base/propertystore/base.py (13619 => 13620)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/base/propertystore/base.py        2014-06-09 16:04:50 UTC (rev 13619)
+++ CalendarServer/trunk/txdav/base/propertystore/base.py        2014-06-09 16:25:14 UTC (rev 13620)
</span><span class="lines">@@ -26,6 +26,7 @@
</span><span class="cx"> from twext.python.log import Logger
</span><span class="cx"> from txdav.xml import element as davxml
</span><span class="cx"> from txdav.xml.base import encodeXMLName
</span><ins>+from txdav.xml.element import lookupElement
</ins><span class="cx"> from txweb2.dav.resource import TwistedGETContentMD5, \
</span><span class="cx"> TwistedQuotaRootProperty
</span><span class="cx">
</span><span class="lines">@@ -98,7 +99,11 @@
</span><span class="cx"> return encodeXMLName(self.namespace, self.name)
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def toElement(self):
+ return lookupElement((self.namespace, self.name,))
</ins><span class="cx">
</span><ins>+
+
</ins><span class="cx"> class AbstractPropertyStore(DictMixin, object):
</span><span class="cx"> """
</span><span class="cx"> Base property store.
</span><span class="lines">@@ -108,6 +113,7 @@
</span><span class="cx"> implements(IPropertyStore)
</span><span class="cx">
</span><span class="cx"> _defaultShadowableKeys = frozenset()
</span><ins>+ _defaultProxyOverrideKeys = frozenset()
</ins><span class="cx"> _defaultGlobalKeys = frozenset((
</span><span class="cx"> PropertyName.fromElement(davxml.ACL),
</span><span class="cx"> PropertyName.fromElement(davxml.ResourceID),
</span><span class="lines">@@ -117,7 +123,7 @@
</span><span class="cx"> PropertyName.fromElement(TwistedQuotaRootProperty),
</span><span class="cx"> ))
</span><span class="cx">
</span><del>- def __init__(self, defaultUser, shareeUser=None):
</del><ins>+ def __init__(self, defaultUser, shareeUser=None, proxyUser=None):
</ins><span class="cx"> """
</span><span class="cx"> Instantiate the property store for a user. The default is the default user
</span><span class="cx"> (owner) property to read in the case of global or shadowable properties.
</span><span class="lines">@@ -128,12 +134,17 @@
</span><span class="cx">
</span><span class="cx"> @param shareeUser: the per user uid or None if the same as defaultUser
</span><span class="cx"> @type shareeUser: C{str}
</span><ins>+
+ @param proxyUser: the proxy uid or None if no proxy
+ @type proxyUser: C{str}
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> assert(defaultUser is not None or shareeUser is not None)
</span><span class="cx"> self._defaultUser = shareeUser if defaultUser is None else defaultUser
</span><span class="cx"> self._perUser = defaultUser if shareeUser is None else shareeUser
</span><ins>+ self._proxyUser = self._perUser if proxyUser is None else proxyUser
</ins><span class="cx"> self._shadowableKeys = set(AbstractPropertyStore._defaultShadowableKeys)
</span><ins>+ self._proxyOverrideKeys = set(AbstractPropertyStore._defaultProxyOverrideKeys)
</ins><span class="cx"> self._globalKeys = set(AbstractPropertyStore._defaultGlobalKeys)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -149,8 +160,13 @@
</span><span class="cx"> self._perUser = uid
</span><span class="cx">
</span><span class="cx">
</span><del>- def setSpecialProperties(self, shadowableKeys, globalKeys):
</del><ins>+ def _setProxyUID(self, uid):
+ self._proxyUser = uid
+
+
+ def setSpecialProperties(self, shadowableKeys, globalKeys, proxyOverrideKeys):
</ins><span class="cx"> self._shadowableKeys.update(shadowableKeys)
</span><ins>+ self._proxyOverrideKeys.update(proxyOverrideKeys)
</ins><span class="cx"> self._globalKeys.update(globalKeys)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -191,6 +207,13 @@
</span><span class="cx"> #
</span><span class="cx">
</span><span class="cx"> def __getitem__(self, key):
</span><ins>+ # Return proxy value if it exists, else fall through to normal logic
+ if self._proxyUser != self._perUser and self.isProxyOverrideProperty(key):
+ try:
+ return self._getitem_uid(key, self._proxyUser)
+ except KeyError:
+ pass
+
</ins><span class="cx"> # Handle per-user behavior
</span><span class="cx"> if self.isShadowableProperty(key):
</span><span class="cx"> try:
</span><span class="lines">@@ -208,11 +231,23 @@
</span><span class="cx"> # Handle per-user behavior
</span><span class="cx"> if self.isGlobalProperty(key):
</span><span class="cx"> return self._setitem_uid(key, value, self._defaultUser)
</span><ins>+ # Handle proxy behavior
+ elif self._proxyUser != self._perUser and self.isProxyOverrideProperty(key):
+ return self._setitem_uid(key, value, self._proxyUser)
+ # Remainder is per user
</ins><span class="cx"> else:
</span><span class="cx"> return self._setitem_uid(key, value, self._perUser)
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def __delitem__(self, key):
</span><ins>+ # Delete proxy value if it exists, else fall through to normal logic
+ if self._proxyUser != self._perUser and self.isProxyOverrideProperty(key):
+ try:
+ self._delitem_uid(key, self._proxyUser)
+ return
+ except KeyError:
+ pass
+
</ins><span class="cx"> # Handle per-user behavior
</span><span class="cx"> if self.isShadowableProperty(key):
</span><span class="cx"> try:
</span><span class="lines">@@ -245,11 +280,14 @@
</span><span class="cx"> self[key] = other[key]
</span><span class="cx">
</span><span class="cx">
</span><del>- # Per-user property handling
</del><span class="cx"> def isShadowableProperty(self, key):
</span><span class="cx"> return key in self._shadowableKeys
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def isProxyOverrideProperty(self, key):
+ return key in self._proxyOverrideKeys
+
+
</ins><span class="cx"> def isGlobalProperty(self, key):
</span><span class="cx"> return key in self._globalKeys
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServertrunktxdavbasepropertystoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/base/propertystore/sql.py (13619 => 13620)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/base/propertystore/sql.py        2014-06-09 16:04:50 UTC (rev 13619)
+++ CalendarServer/trunk/txdav/base/propertystore/sql.py        2014-06-09 16:25:14 UTC (rev 13620)
</span><span class="lines">@@ -117,17 +117,19 @@
</span><span class="cx"> yield _cache_user_props(self._defaultUser)
</span><span class="cx"> if self._perUser != self._defaultUser:
</span><span class="cx"> yield _cache_user_props(self._perUser)
</span><ins>+ if self._proxyUser != self._perUser:
+ yield _cache_user_props(self._proxyUser)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @classmethod
</span><span class="cx"> @inlineCallbacks
</span><del>- def load(cls, defaultuser, shareUser, txn, resourceID, created=False, notifyCallback=None):
</del><ins>+ def load(cls, defaultuser, shareUser, proxyUser, txn, resourceID, created=False, notifyCallback=None):
</ins><span class="cx"> """
</span><span class="cx"> @param notifyCallback: a callable used to trigger notifications when the
</span><span class="cx"> property store changes.
</span><span class="cx"> """
</span><span class="cx"> self = cls.__new__(cls)
</span><del>- super(PropertyStore, self).__init__(defaultuser, shareUser)
</del><ins>+ super(PropertyStore, self).__init__(defaultuser, shareUser, proxyUser)
</ins><span class="cx"> self._txn = txn
</span><span class="cx"> self._resourceID = resourceID
</span><span class="cx"> if not self._txn.store().queryCachingEnabled():
</span><span class="lines">@@ -141,7 +143,7 @@
</span><span class="cx">
</span><span class="cx"> @classmethod
</span><span class="cx"> @inlineCallbacks
</span><del>- def forMultipleResources(cls, defaultUser, txn,
</del><ins>+ def forMultipleResources(cls, defaultUser, shareeUser, proxyUser, txn,
</ins><span class="cx"> childColumn, parentColumn, parentID):
</span><span class="cx"> """
</span><span class="cx"> Load all property stores for all objects in a collection. This is used
</span><span class="lines">@@ -179,13 +181,13 @@
</span><span class="cx"> Where=parentColumn == parentID
</span><span class="cx"> )
</span><span class="cx"> rows = yield query.on(txn)
</span><del>- stores = cls._createMultipleStores(defaultUser, txn, rows)
</del><ins>+ stores = cls._createMultipleStores(defaultUser, shareeUser, proxyUser, txn, rows)
</ins><span class="cx"> returnValue(stores)
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @classmethod
</span><span class="cx"> @inlineCallbacks
</span><del>- def forMultipleResourcesWithResourceIDs(cls, defaultUser, txn, resourceIDs):
</del><ins>+ def forMultipleResourcesWithResourceIDs(cls, defaultUser, shareeUser, proxyUser, txn, resourceIDs):
</ins><span class="cx"> """
</span><span class="cx"> Load all property stores for all specified resources. This is used
</span><span class="cx"> to optimize Depth:1 operations on that collection, by loading all
</span><span class="lines">@@ -214,13 +216,13 @@
</span><span class="cx"> Where=prop.RESOURCE_ID.In(Parameter("resourceIDs", len(resourceIDs)))
</span><span class="cx"> )
</span><span class="cx"> rows = yield query.on(txn, resourceIDs=resourceIDs)
</span><del>- stores = cls._createMultipleStores(defaultUser, txn, rows)
</del><ins>+ stores = cls._createMultipleStores(defaultUser, shareeUser, proxyUser, txn, rows)
</ins><span class="cx">
</span><span class="cx"> # Make sure we have a store for each resourceID even if no properties exist
</span><span class="cx"> for resourceID in resourceIDs:
</span><span class="cx"> if resourceID not in stores:
</span><span class="cx"> store = cls.__new__(cls)
</span><del>- super(PropertyStore, store).__init__(defaultUser)
</del><ins>+ super(PropertyStore, store).__init__(defaultUser, shareeUser, proxyUser)
</ins><span class="cx"> store._txn = txn
</span><span class="cx"> store._resourceID = resourceID
</span><span class="cx"> store._cached = {}
</span><span class="lines">@@ -230,7 +232,7 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @classmethod
</span><del>- def _createMultipleStores(cls, defaultUser, txn, rows):
</del><ins>+ def _createMultipleStores(cls, defaultUser, shareeUser, proxyUser, txn, rows):
</ins><span class="cx"> """
</span><span class="cx"> Create a set of stores for the set of rows passed in.
</span><span class="cx"> """
</span><span class="lines">@@ -245,7 +247,7 @@
</span><span class="cx"> if resource_id:
</span><span class="cx"> if resource_id not in createdStores:
</span><span class="cx"> store = cls.__new__(cls)
</span><del>- super(PropertyStore, store).__init__(defaultUser)
</del><ins>+ super(PropertyStore, store).__init__(defaultUser, shareeUser, proxyUser)
</ins><span class="cx"> store._txn = txn
</span><span class="cx"> store._resourceID = resource_id
</span><span class="cx"> store._cached = {}
</span><span class="lines">@@ -253,7 +255,7 @@
</span><span class="cx"> createdStores[resource_id]._cached[(name, view_uid)] = value
</span><span class="cx"> elif object_resource_id:
</span><span class="cx"> store = cls.__new__(cls)
</span><del>- super(PropertyStore, store).__init__(defaultUser)
</del><ins>+ super(PropertyStore, store).__init__(defaultUser, shareeUser, proxyUser)
</ins><span class="cx"> store._txn = txn
</span><span class="cx"> store._resourceID = object_resource_id
</span><span class="cx"> store._cached = {}
</span></span></pre></div>
<a id="CalendarServertrunktxdavbasepropertystoretestbasepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/base/propertystore/test/base.py (13619 => 13620)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/base/propertystore/test/base.py        2014-06-09 16:04:50 UTC (rev 13619)
+++ CalendarServer/trunk/txdav/base/propertystore/test/base.py        2014-06-09 16:25:14 UTC (rev 13620)
</span><span class="lines">@@ -140,29 +140,45 @@
</span><span class="cx"> yield self._changed(self.propertyStore1)
</span><span class="cx"> self.assertEquals(self.propertyStore1.get(name, None), value1)
</span><span class="cx"> self.assertEquals(self.propertyStore2.get(name, None), None)
</span><ins>+ self.assertEquals(self.propertyStore3.get(name, None), value1)
+ self.assertEquals(self.propertyStore4.get(name, None), None)
</ins><span class="cx"> self.failUnless(name in self.propertyStore1)
</span><span class="cx"> self.failIf(name in self.propertyStore2)
</span><ins>+ self.failUnless(name in self.propertyStore3)
+ self.failIf(name in self.propertyStore4)
</ins><span class="cx">
</span><span class="cx"> self.propertyStore2[name] = value2
</span><span class="cx"> yield self._changed(self.propertyStore2)
</span><span class="cx"> self.assertEquals(self.propertyStore1.get(name, None), value1)
</span><span class="cx"> self.assertEquals(self.propertyStore2.get(name, None), value2)
</span><ins>+ self.assertEquals(self.propertyStore3.get(name, None), value1)
+ self.assertEquals(self.propertyStore4.get(name, None), value2)
</ins><span class="cx"> self.failUnless(name in self.propertyStore1)
</span><span class="cx"> self.failUnless(name in self.propertyStore2)
</span><ins>+ self.failUnless(name in self.propertyStore3)
+ self.failUnless(name in self.propertyStore4)
</ins><span class="cx">
</span><span class="cx"> del self.propertyStore2[name]
</span><span class="cx"> yield self._changed(self.propertyStore2)
</span><span class="cx"> self.assertEquals(self.propertyStore1.get(name, None), value1)
</span><span class="cx"> self.assertEquals(self.propertyStore2.get(name, None), None)
</span><ins>+ self.assertEquals(self.propertyStore3.get(name, None), value1)
+ self.assertEquals(self.propertyStore4.get(name, None), None)
</ins><span class="cx"> self.failUnless(name in self.propertyStore1)
</span><span class="cx"> self.failIf(name in self.propertyStore2)
</span><ins>+ self.failUnless(name in self.propertyStore3)
+ self.failIf(name in self.propertyStore4)
</ins><span class="cx">
</span><span class="cx"> del self.propertyStore1[name]
</span><span class="cx"> yield self._changed(self.propertyStore1)
</span><span class="cx"> self.assertEquals(self.propertyStore1.get(name, None), None)
</span><span class="cx"> self.assertEquals(self.propertyStore2.get(name, None), None)
</span><ins>+ self.assertEquals(self.propertyStore3.get(name, None), None)
+ self.assertEquals(self.propertyStore4.get(name, None), None)
</ins><span class="cx"> self.failIf(name in self.propertyStore1)
</span><span class="cx"> self.failIf(name in self.propertyStore2)
</span><ins>+ self.failIf(name in self.propertyStore3)
+ self.failIf(name in self.propertyStore4)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -170,8 +186,10 @@
</span><span class="cx">
</span><span class="cx"> name = propertyName("shadow")
</span><span class="cx">
</span><del>- self.propertyStore1.setSpecialProperties((name,), ())
- self.propertyStore2.setSpecialProperties((name,), ())
</del><ins>+ self.propertyStore1.setSpecialProperties((name,), (), ())
+ self.propertyStore2.setSpecialProperties((name,), (), ())
+ self.propertyStore3.setSpecialProperties((name,), (), ())
+ self.propertyStore4.setSpecialProperties((name,), (), ())
</ins><span class="cx">
</span><span class="cx"> value1 = propertyValue("Hello, World1!")
</span><span class="cx"> value2 = propertyValue("Hello, World2!")
</span><span class="lines">@@ -180,29 +198,45 @@
</span><span class="cx"> yield self._changed(self.propertyStore1)
</span><span class="cx"> self.assertEquals(self.propertyStore1.get(name, None), value1)
</span><span class="cx"> self.assertEquals(self.propertyStore2.get(name, None), value1)
</span><ins>+ self.assertEquals(self.propertyStore3.get(name, None), value1)
+ self.assertEquals(self.propertyStore4.get(name, None), value1)
</ins><span class="cx"> self.failUnless(name in self.propertyStore1)
</span><span class="cx"> self.failUnless(name in self.propertyStore2)
</span><ins>+ self.failUnless(name in self.propertyStore3)
+ self.failUnless(name in self.propertyStore4)
</ins><span class="cx">
</span><span class="cx"> self.propertyStore2[name] = value2
</span><span class="cx"> yield self._changed(self.propertyStore2)
</span><span class="cx"> self.assertEquals(self.propertyStore1.get(name, None), value1)
</span><span class="cx"> self.assertEquals(self.propertyStore2.get(name, None), value2)
</span><ins>+ self.assertEquals(self.propertyStore3.get(name, None), value1)
+ self.assertEquals(self.propertyStore4.get(name, None), value2)
</ins><span class="cx"> self.failUnless(name in self.propertyStore1)
</span><span class="cx"> self.failUnless(name in self.propertyStore2)
</span><ins>+ self.failUnless(name in self.propertyStore3)
+ self.failUnless(name in self.propertyStore4)
</ins><span class="cx">
</span><span class="cx"> del self.propertyStore2[name]
</span><span class="cx"> yield self._changed(self.propertyStore2)
</span><span class="cx"> self.assertEquals(self.propertyStore1.get(name, None), value1)
</span><span class="cx"> self.assertEquals(self.propertyStore2.get(name, None), value1)
</span><ins>+ self.assertEquals(self.propertyStore3.get(name, None), value1)
+ self.assertEquals(self.propertyStore4.get(name, None), value1)
</ins><span class="cx"> self.failUnless(name in self.propertyStore1)
</span><span class="cx"> self.failUnless(name in self.propertyStore2)
</span><ins>+ self.failUnless(name in self.propertyStore3)
+ self.failUnless(name in self.propertyStore4)
</ins><span class="cx">
</span><span class="cx"> del self.propertyStore1[name]
</span><span class="cx"> yield self._changed(self.propertyStore1)
</span><span class="cx"> self.assertEquals(self.propertyStore1.get(name, None), None)
</span><span class="cx"> self.assertEquals(self.propertyStore2.get(name, None), None)
</span><ins>+ self.assertEquals(self.propertyStore3.get(name, None), None)
+ self.assertEquals(self.propertyStore4.get(name, None), None)
</ins><span class="cx"> self.failIf(name in self.propertyStore1)
</span><span class="cx"> self.failIf(name in self.propertyStore2)
</span><ins>+ self.failIf(name in self.propertyStore3)
+ self.failIf(name in self.propertyStore4)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -213,8 +247,10 @@
</span><span class="cx">
</span><span class="cx"> name = propertyName("shadow")
</span><span class="cx">
</span><del>- self.propertyStore1.setSpecialProperties((name,), ())
- self.propertyStore2.setSpecialProperties((name,), ())
</del><ins>+ self.propertyStore1.setSpecialProperties((name,), (), ())
+ self.propertyStore2.setSpecialProperties((name,), (), ())
+ self.propertyStore3.setSpecialProperties((name,), (), ())
+ self.propertyStore4.setSpecialProperties((name,), (), ())
</ins><span class="cx">
</span><span class="cx"> value1 = propertyValue("Hello, World1!")
</span><span class="cx">
</span><span class="lines">@@ -222,22 +258,34 @@
</span><span class="cx"> yield self._changed(self.propertyStore1)
</span><span class="cx"> self.assertEquals(self.propertyStore1.get(name, None), value1)
</span><span class="cx"> self.assertEquals(self.propertyStore2.get(name, None), value1)
</span><ins>+ self.assertEquals(self.propertyStore3.get(name, None), value1)
+ self.assertEquals(self.propertyStore4.get(name, None), value1)
</ins><span class="cx"> self.failUnless(name in self.propertyStore1)
</span><span class="cx"> self.failUnless(name in self.propertyStore2)
</span><ins>+ self.failUnless(name in self.propertyStore3)
+ self.failUnless(name in self.propertyStore4)
</ins><span class="cx">
</span><span class="cx"> del self.propertyStore2[name]
</span><span class="cx"> yield self._changed(self.propertyStore2)
</span><span class="cx"> self.assertEquals(self.propertyStore1.get(name, None), value1)
</span><span class="cx"> self.assertEquals(self.propertyStore2.get(name, None), value1)
</span><ins>+ self.assertEquals(self.propertyStore3.get(name, None), value1)
+ self.assertEquals(self.propertyStore4.get(name, None), value1)
</ins><span class="cx"> self.failUnless(name in self.propertyStore1)
</span><span class="cx"> self.failUnless(name in self.propertyStore2)
</span><ins>+ self.failUnless(name in self.propertyStore3)
+ self.failUnless(name in self.propertyStore4)
</ins><span class="cx">
</span><span class="cx"> del self.propertyStore1[name]
</span><span class="cx"> yield self._changed(self.propertyStore1)
</span><span class="cx"> self.assertEquals(self.propertyStore1.get(name, None), None)
</span><span class="cx"> self.assertEquals(self.propertyStore2.get(name, None), None)
</span><ins>+ self.assertEquals(self.propertyStore3.get(name, None), None)
+ self.assertEquals(self.propertyStore4.get(name, None), None)
</ins><span class="cx"> self.failIf(name in self.propertyStore1)
</span><span class="cx"> self.failIf(name in self.propertyStore2)
</span><ins>+ self.failIf(name in self.propertyStore3)
+ self.failIf(name in self.propertyStore4)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -245,8 +293,10 @@
</span><span class="cx">
</span><span class="cx"> name = propertyName("global")
</span><span class="cx">
</span><del>- self.propertyStore1.setSpecialProperties((), (name,))
- self.propertyStore2.setSpecialProperties((), (name,))
</del><ins>+ self.propertyStore1.setSpecialProperties((), (name,), ())
+ self.propertyStore2.setSpecialProperties((), (name,), ())
+ self.propertyStore3.setSpecialProperties((), (name,), ())
+ self.propertyStore4.setSpecialProperties((), (name,), ())
</ins><span class="cx">
</span><span class="cx"> value1 = propertyValue("Hello, World1!")
</span><span class="cx"> value2 = propertyValue("Hello, World2!")
</span><span class="lines">@@ -255,24 +305,250 @@
</span><span class="cx"> yield self._changed(self.propertyStore1)
</span><span class="cx"> self.assertEquals(self.propertyStore1.get(name, None), value1)
</span><span class="cx"> self.assertEquals(self.propertyStore2.get(name, None), value1)
</span><ins>+ self.assertEquals(self.propertyStore3.get(name, None), value1)
+ self.assertEquals(self.propertyStore4.get(name, None), value1)
</ins><span class="cx"> self.failUnless(name in self.propertyStore1)
</span><span class="cx"> self.failUnless(name in self.propertyStore2)
</span><ins>+ self.failUnless(name in self.propertyStore3)
+ self.failUnless(name in self.propertyStore4)
</ins><span class="cx">
</span><span class="cx"> self.propertyStore2[name] = value2
</span><span class="cx"> yield self._changed(self.propertyStore2)
</span><span class="cx"> self.assertEquals(self.propertyStore1.get(name, None), value2)
</span><span class="cx"> self.assertEquals(self.propertyStore2.get(name, None), value2)
</span><ins>+ self.assertEquals(self.propertyStore3.get(name, None), value2)
+ self.assertEquals(self.propertyStore4.get(name, None), value2)
</ins><span class="cx"> self.failUnless(name in self.propertyStore1)
</span><span class="cx"> self.failUnless(name in self.propertyStore2)
</span><ins>+ self.failUnless(name in self.propertyStore3)
+ self.failUnless(name in self.propertyStore4)
</ins><span class="cx">
</span><span class="cx"> del self.propertyStore2[name]
</span><span class="cx"> yield self._changed(self.propertyStore2)
</span><span class="cx"> self.assertEquals(self.propertyStore1.get(name, None), None)
</span><span class="cx"> self.assertEquals(self.propertyStore2.get(name, None), None)
</span><ins>+ self.assertEquals(self.propertyStore3.get(name, None), None)
+ self.assertEquals(self.propertyStore4.get(name, None), None)
</ins><span class="cx"> self.failIf(name in self.propertyStore1)
</span><span class="cx"> self.failIf(name in self.propertyStore2)
</span><ins>+ self.failIf(name in self.propertyStore3)
+ self.failIf(name in self.propertyStore4)
</ins><span class="cx">
</span><span class="cx">
</span><ins>+ @inlineCallbacks
+ def test_proxy(self):
+
+ name = propertyName("test")
+ value1 = propertyValue("Hello, World1!")
+ value2 = propertyValue("Hello, World2!")
+
+ self.propertyStore3[name] = value1
+ yield self._changed(self.propertyStore3)
+ self.assertEquals(self.propertyStore1.get(name, None), value1)
+ self.assertEquals(self.propertyStore2.get(name, None), None)
+ self.assertEquals(self.propertyStore3.get(name, None), value1)
+ self.assertEquals(self.propertyStore4.get(name, None), None)
+ self.failUnless(name in self.propertyStore1)
+ self.failIf(name in self.propertyStore2)
+ self.failUnless(name in self.propertyStore3)
+ self.failIf(name in self.propertyStore4)
+
+ self.propertyStore4[name] = value2
+ yield self._changed(self.propertyStore4)
+ self.assertEquals(self.propertyStore1.get(name, None), value1)
+ self.assertEquals(self.propertyStore2.get(name, None), value2)
+ self.assertEquals(self.propertyStore3.get(name, None), value1)
+ self.assertEquals(self.propertyStore4.get(name, None), value2)
+ self.failUnless(name in self.propertyStore1)
+ self.failUnless(name in self.propertyStore2)
+ self.failUnless(name in self.propertyStore3)
+ self.failUnless(name in self.propertyStore4)
+
+ del self.propertyStore4[name]
+ yield self._changed(self.propertyStore4)
+ self.assertEquals(self.propertyStore1.get(name, None), value1)
+ self.assertEquals(self.propertyStore2.get(name, None), None)
+ self.assertEquals(self.propertyStore3.get(name, None), value1)
+ self.assertEquals(self.propertyStore4.get(name, None), None)
+ self.failUnless(name in self.propertyStore1)
+ self.failIf(name in self.propertyStore2)
+ self.failUnless(name in self.propertyStore3)
+ self.failIf(name in self.propertyStore4)
+
+ del self.propertyStore3[name]
+ yield self._changed(self.propertyStore3)
+ self.assertEquals(self.propertyStore1.get(name, None), None)
+ self.assertEquals(self.propertyStore2.get(name, None), None)
+ self.assertEquals(self.propertyStore3.get(name, None), None)
+ self.assertEquals(self.propertyStore4.get(name, None), None)
+ self.failIf(name in self.propertyStore1)
+ self.failIf(name in self.propertyStore2)
+ self.failIf(name in self.propertyStore3)
+ self.failIf(name in self.propertyStore4)
+
+
+ @inlineCallbacks
+ def test_proxyOverride(self):
+
+ name = propertyName("override")
+
+ self.propertyStore1.setSpecialProperties((), (), (name,))
+ self.propertyStore2.setSpecialProperties((), (), (name,))
+ self.propertyStore3.setSpecialProperties((), (), (name,))
+ self.propertyStore4.setSpecialProperties((), (), (name,))
+
+ value1 = propertyValue("Hello, World1!")
+ value2 = propertyValue("Hello, World2!")
+
+ self.propertyStore1[name] = value1
+ yield self._changed(self.propertyStore1)
+ self.assertEquals(self.propertyStore1.get(name, None), value1)
+ self.assertEquals(self.propertyStore2.get(name, None), None)
+ self.assertEquals(self.propertyStore3.get(name, None), value1)
+ self.assertEquals(self.propertyStore4.get(name, None), None)
+ self.failUnless(name in self.propertyStore1)
+ self.failIf(name in self.propertyStore2)
+ self.failUnless(name in self.propertyStore3)
+ self.failIf(name in self.propertyStore4)
+
+ self.propertyStore3[name] = value2
+ yield self._changed(self.propertyStore3)
+ self.assertEquals(self.propertyStore1.get(name, None), value1)
+ self.assertEquals(self.propertyStore2.get(name, None), None)
+ self.assertEquals(self.propertyStore3.get(name, None), value2)
+ self.assertEquals(self.propertyStore4.get(name, None), None)
+ self.failUnless(name in self.propertyStore1)
+ self.failIf(name in self.propertyStore2)
+ self.failUnless(name in self.propertyStore3)
+ self.failIf(name in self.propertyStore4)
+
+ del self.propertyStore3[name]
+ yield self._changed(self.propertyStore3)
+ self.assertEquals(self.propertyStore1.get(name, None), value1)
+ self.assertEquals(self.propertyStore2.get(name, None), None)
+ self.assertEquals(self.propertyStore3.get(name, None), value1)
+ self.assertEquals(self.propertyStore4.get(name, None), None)
+ self.failUnless(name in self.propertyStore1)
+ self.failIf(name in self.propertyStore2)
+ self.failUnless(name in self.propertyStore3)
+ self.failIf(name in self.propertyStore4)
+
+ del self.propertyStore1[name]
+ yield self._changed(self.propertyStore1)
+ self.assertEquals(self.propertyStore1.get(name, None), None)
+ self.assertEquals(self.propertyStore2.get(name, None), None)
+ self.assertEquals(self.propertyStore3.get(name, None), None)
+ self.assertEquals(self.propertyStore4.get(name, None), None)
+ self.failIf(name in self.propertyStore1)
+ self.failIf(name in self.propertyStore2)
+ self.failIf(name in self.propertyStore3)
+ self.failIf(name in self.propertyStore4)
+
+
+ @inlineCallbacks
+ def test_proxyOverrideShadow(self):
+
+ name = propertyName("override")
+
+ self.propertyStore1.setSpecialProperties((name,), (), (name,))
+ self.propertyStore2.setSpecialProperties((name,), (), (name,))
+ self.propertyStore3.setSpecialProperties((name,), (), (name,))
+ self.propertyStore4.setSpecialProperties((name,), (), (name,))
+
+ value1 = propertyValue("Hello, World1!")
+ value2 = propertyValue("Hello, World2!")
+ value3 = propertyValue("Hello, World3!")
+ value4 = propertyValue("Hello, World4!")
+
+ self.propertyStore1[name] = value1
+ yield self._changed(self.propertyStore1)
+ self.assertEquals(self.propertyStore1.get(name, None), value1)
+ self.assertEquals(self.propertyStore2.get(name, None), value1)
+ self.assertEquals(self.propertyStore3.get(name, None), value1)
+ self.assertEquals(self.propertyStore4.get(name, None), value1)
+ self.failUnless(name in self.propertyStore1)
+ self.failUnless(name in self.propertyStore2)
+ self.failUnless(name in self.propertyStore3)
+ self.failUnless(name in self.propertyStore4)
+
+ self.propertyStore3[name] = value3
+ yield self._changed(self.propertyStore3)
+ self.assertEquals(self.propertyStore1.get(name, None), value1)
+ self.assertEquals(self.propertyStore2.get(name, None), value1)
+ self.assertEquals(self.propertyStore3.get(name, None), value3)
+ self.assertEquals(self.propertyStore4.get(name, None), value1)
+ self.failUnless(name in self.propertyStore1)
+ self.failUnless(name in self.propertyStore2)
+ self.failUnless(name in self.propertyStore3)
+ self.failUnless(name in self.propertyStore4)
+
+ self.propertyStore4[name] = value4
+ yield self._changed(self.propertyStore4)
+ self.assertEquals(self.propertyStore1.get(name, None), value1)
+ self.assertEquals(self.propertyStore2.get(name, None), value1)
+ self.assertEquals(self.propertyStore3.get(name, None), value3)
+ self.assertEquals(self.propertyStore4.get(name, None), value4)
+ self.failUnless(name in self.propertyStore1)
+ self.failUnless(name in self.propertyStore2)
+ self.failUnless(name in self.propertyStore3)
+ self.failUnless(name in self.propertyStore4)
+
+ self.propertyStore2[name] = value2
+ yield self._changed(self.propertyStore2)
+ self.assertEquals(self.propertyStore1.get(name, None), value1)
+ self.assertEquals(self.propertyStore2.get(name, None), value2)
+ self.assertEquals(self.propertyStore3.get(name, None), value3)
+ self.assertEquals(self.propertyStore4.get(name, None), value4)
+ self.failUnless(name in self.propertyStore1)
+ self.failUnless(name in self.propertyStore2)
+ self.failUnless(name in self.propertyStore3)
+ self.failUnless(name in self.propertyStore4)
+
+ del self.propertyStore3[name]
+ yield self._changed(self.propertyStore3)
+ self.assertEquals(self.propertyStore1.get(name, None), value1)
+ self.assertEquals(self.propertyStore2.get(name, None), value2)
+ self.assertEquals(self.propertyStore3.get(name, None), value1)
+ self.assertEquals(self.propertyStore4.get(name, None), value4)
+ self.failUnless(name in self.propertyStore1)
+ self.failUnless(name in self.propertyStore2)
+ self.failUnless(name in self.propertyStore3)
+ self.failUnless(name in self.propertyStore4)
+
+ del self.propertyStore4[name]
+ yield self._changed(self.propertyStore4)
+ self.assertEquals(self.propertyStore1.get(name, None), value1)
+ self.assertEquals(self.propertyStore2.get(name, None), value2)
+ self.assertEquals(self.propertyStore3.get(name, None), value1)
+ self.assertEquals(self.propertyStore4.get(name, None), value2)
+ self.failUnless(name in self.propertyStore1)
+ self.failUnless(name in self.propertyStore2)
+ self.failUnless(name in self.propertyStore3)
+ self.failUnless(name in self.propertyStore4)
+
+ del self.propertyStore2[name]
+ yield self._changed(self.propertyStore2)
+ self.assertEquals(self.propertyStore1.get(name, None), value1)
+ self.assertEquals(self.propertyStore2.get(name, None), value1)
+ self.assertEquals(self.propertyStore3.get(name, None), value1)
+ self.assertEquals(self.propertyStore4.get(name, None), value1)
+ self.failUnless(name in self.propertyStore1)
+ self.failUnless(name in self.propertyStore2)
+ self.failUnless(name in self.propertyStore3)
+ self.failUnless(name in self.propertyStore4)
+
+ del self.propertyStore1[name]
+ yield self._changed(self.propertyStore1)
+ self.assertEquals(self.propertyStore1.get(name, None), None)
+ self.assertEquals(self.propertyStore2.get(name, None), None)
+ self.assertEquals(self.propertyStore3.get(name, None), None)
+ self.assertEquals(self.propertyStore4.get(name, None), None)
+ self.failIf(name in self.propertyStore1)
+ self.failIf(name in self.propertyStore2)
+ self.failIf(name in self.propertyStore3)
+ self.failIf(name in self.propertyStore4)
+
+
</ins><span class="cx"> def test_iteration(self):
</span><span class="cx">
</span><span class="cx"> value = propertyValue("Hello, World!")
</span><span class="lines">@@ -333,8 +609,8 @@
</span><span class="cx">
</span><span class="cx"> name = propertyName("shadow")
</span><span class="cx">
</span><del>- self.propertyStore1.setSpecialProperties((name,), ())
- self.propertyStore2.setSpecialProperties((name,), ())
</del><ins>+ self.propertyStore1.setSpecialProperties((name,), (), ())
+ self.propertyStore2.setSpecialProperties((name,), (), ())
</ins><span class="cx">
</span><span class="cx"> value1 = propertyValue("Hello, World1!")
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServertrunktxdavbasepropertystoretesttest_sqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/base/propertystore/test/test_sql.py (13619 => 13620)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/base/propertystore/test/test_sql.py        2014-06-09 16:04:50 UTC (rev 13619)
+++ CalendarServer/trunk/txdav/base/propertystore/test/test_sql.py        2014-06-09 16:25:14 UTC (rev 13620)
</span><span class="lines">@@ -49,10 +49,11 @@
</span><span class="cx"> self.store = yield buildStore(self, self.notifierFactory)
</span><span class="cx"> self.addCleanup(self.maybeCommitLast)
</span><span class="cx"> self._txn = self.store.newTransaction()
</span><del>- self.propertyStore = self.propertyStore1 = yield PropertyStore.load(
- "user01", None, self._txn, 1
- )
- self.propertyStore2 = yield PropertyStore.load("user01", "user02", self._txn, 1)
</del><ins>+ self.propertyStore = \
+ self.propertyStore1 = yield PropertyStore.load("user01", None, None, self._txn, 1)
+ self.propertyStore2 = yield PropertyStore.load("user01", "user02", None, self._txn, 1)
+ self.propertyStore3 = yield PropertyStore.load("user01", None, "user03", self._txn, 1)
+ self.propertyStore4 = yield PropertyStore.load("user01", "user02", "user04", self._txn, 1)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -62,7 +63,11 @@
</span><span class="cx"> delattr(self, "_txn")
</span><span class="cx"> else:
</span><span class="cx"> result = None
</span><del>- self.propertyStore = self.propertyStore1 = self.propertyStore2 = None
</del><ins>+ self.propertyStore = \
+ self.propertyStore1 = \
+ self.propertyStore2 = \
+ self.propertyStore3 = \
+ self.propertyStore4 = None
</ins><span class="cx"> returnValue(result)
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -74,18 +79,31 @@
</span><span class="cx"> self._txn = self.store.newTransaction()
</span><span class="cx">
</span><span class="cx"> store = self.propertyStore1
</span><del>- self.propertyStore = self.propertyStore1 = yield PropertyStore.load(
- "user01", None, self._txn, 1
- )
</del><ins>+ self.propertyStore = \
+ self.propertyStore1 = yield PropertyStore.load("user01", None, None, self._txn, 1)
</ins><span class="cx"> self.propertyStore1._shadowableKeys = store._shadowableKeys
</span><ins>+ self.propertyStore1._proxyOverrideKeys = store._proxyOverrideKeys
</ins><span class="cx"> self.propertyStore1._globalKeys = store._globalKeys
</span><span class="cx">
</span><span class="cx"> store = self.propertyStore2
</span><del>- self.propertyStore2 = yield PropertyStore.load("user01", "user02", self._txn, 1)
</del><ins>+ self.propertyStore2 = yield PropertyStore.load("user01", "user02", None, self._txn, 1)
</ins><span class="cx"> self.propertyStore2._shadowableKeys = store._shadowableKeys
</span><ins>+ self.propertyStore2._proxyOverrideKeys = store._proxyOverrideKeys
</ins><span class="cx"> self.propertyStore2._globalKeys = store._globalKeys
</span><span class="cx">
</span><ins>+ store = self.propertyStore3
+ self.propertyStore3 = yield PropertyStore.load("user01", None, "user03", self._txn, 1)
+ self.propertyStore3._shadowableKeys = store._shadowableKeys
+ self.propertyStore3._proxyOverrideKeys = store._proxyOverrideKeys
+ self.propertyStore3._globalKeys = store._globalKeys
</ins><span class="cx">
</span><ins>+ store = self.propertyStore4
+ self.propertyStore4 = yield PropertyStore.load("user01", "user02", "user04", self._txn, 1)
+ self.propertyStore4._shadowableKeys = store._shadowableKeys
+ self.propertyStore4._proxyOverrideKeys = store._proxyOverrideKeys
+ self.propertyStore4._globalKeys = store._globalKeys
+
+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _abort(self, store):
</span><span class="cx"> if hasattr(self, "_txn"):
</span><span class="lines">@@ -95,18 +113,31 @@
</span><span class="cx"> self._txn = self.store.newTransaction()
</span><span class="cx">
</span><span class="cx"> store = self.propertyStore1
</span><del>- self.propertyStore = self.propertyStore1 = yield PropertyStore.load(
- "user01", None, self._txn, 1
- )
</del><ins>+ self.propertyStore = \
+ self.propertyStore1 = yield PropertyStore.load("user01", None, None, self._txn, 1)
</ins><span class="cx"> self.propertyStore1._shadowableKeys = store._shadowableKeys
</span><ins>+ self.propertyStore1._proxyOverrideKeys = store._proxyOverrideKeys
</ins><span class="cx"> self.propertyStore1._globalKeys = store._globalKeys
</span><span class="cx">
</span><span class="cx"> store = self.propertyStore2
</span><del>- self.propertyStore2 = yield PropertyStore.load("user01", "user02", self._txn, 1)
</del><ins>+ self.propertyStore2 = yield PropertyStore.load("user01", "user02", None, self._txn, 1)
</ins><span class="cx"> self.propertyStore2._shadowableKeys = store._shadowableKeys
</span><ins>+ self.propertyStore2._proxyOverrideKeys = store._proxyOverrideKeys
</ins><span class="cx"> self.propertyStore2._globalKeys = store._globalKeys
</span><span class="cx">
</span><ins>+ store = self.propertyStore3
+ self.propertyStore3 = yield PropertyStore.load("user01", None, "user03", self._txn, 1)
+ self.propertyStore3._shadowableKeys = store._shadowableKeys
+ self.propertyStore3._proxyOverrideKeys = store._proxyOverrideKeys
+ self.propertyStore3._globalKeys = store._globalKeys
</ins><span class="cx">
</span><ins>+ store = self.propertyStore4
+ self.propertyStore4 = yield PropertyStore.load("user01", "user02", "user04", self._txn, 1)
+ self.propertyStore4._shadowableKeys = store._shadowableKeys
+ self.propertyStore4._proxyOverrideKeys = store._proxyOverrideKeys
+ self.propertyStore4._globalKeys = store._globalKeys
+
+
</ins><span class="cx"> @inlineCallbacks
</span><span class="cx"> def test_concurrentInsertion(self):
</span><span class="cx"> """
</span><span class="lines">@@ -126,7 +157,7 @@
</span><span class="cx"> pass
</span><span class="cx"> self.addCleanup(maybeAbortIt)
</span><span class="cx"> concurrentPropertyStore = yield PropertyStore.load(
</span><del>- "user01", None, concurrentTxn, 1
</del><ins>+ "user01", None, None, concurrentTxn, 1
</ins><span class="cx"> )
</span><span class="cx"> concurrentPropertyStore[pname] = pval1
</span><span class="cx"> race = []
</span><span class="lines">@@ -155,8 +186,8 @@
</span><span class="cx"> def test_copy(self):
</span><span class="cx">
</span><span class="cx"> # Existing store
</span><del>- store1_user1 = yield PropertyStore.load("user01", None, self._txn, 2)
- store1_user2 = yield PropertyStore.load("user01", "user02", self._txn, 2)
</del><ins>+ store1_user1 = yield PropertyStore.load("user01", None, None, self._txn, 2)
+ store1_user2 = yield PropertyStore.load("user01", "user02", None, self._txn, 2)
</ins><span class="cx">
</span><span class="cx"> # Populate current store with data
</span><span class="cx"> props_user1 = (
</span><span class="lines">@@ -178,18 +209,18 @@
</span><span class="cx"> self._txn = self.store.newTransaction()
</span><span class="cx">
</span><span class="cx"> # Existing store
</span><del>- store1_user1 = yield PropertyStore.load("user01", None, self._txn, 2)
</del><ins>+ store1_user1 = yield PropertyStore.load("user01", None, None, self._txn, 2)
</ins><span class="cx">
</span><span class="cx"> # New store
</span><del>- store2_user1 = yield PropertyStore.load("user01", None, self._txn, 3)
</del><ins>+ store2_user1 = yield PropertyStore.load("user01", None, None, self._txn, 3)
</ins><span class="cx">
</span><span class="cx"> # Do copy and check results
</span><span class="cx"> yield store2_user1.copyAllProperties(store1_user1)
</span><span class="cx">
</span><span class="cx"> self.assertEqual(store1_user1.keys(), store2_user1.keys())
</span><span class="cx">
</span><del>- store1_user2 = yield PropertyStore.load("user01", "user02", self._txn, 2)
- store2_user2 = yield PropertyStore.load("user01", "user02", self._txn, 3)
</del><ins>+ store1_user2 = yield PropertyStore.load("user01", "user02", None, self._txn, 2)
+ store2_user2 = yield PropertyStore.load("user01", "user02", None, self._txn, 3)
</ins><span class="cx"> self.assertEqual(store1_user2.keys(), store2_user2.keys())
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -197,7 +228,7 @@
</span><span class="cx"> def test_insert_delete(self):
</span><span class="cx">
</span><span class="cx"> # Existing store
</span><del>- store1_user1 = yield PropertyStore.load("user01", None, self._txn, 2)
</del><ins>+ store1_user1 = yield PropertyStore.load("user01", None, None, self._txn, 2)
</ins><span class="cx">
</span><span class="cx"> pname = propertyName("dummy1")
</span><span class="cx"> pvalue = propertyValue("value1-user1")
</span><span class="lines">@@ -221,7 +252,7 @@
</span><span class="cx">
</span><span class="cx"> # Existing store - add a normal property
</span><span class="cx"> self.assertFalse("SQL.props:10/user01" in PropertyStore._cacher._memcacheProtocol._cache)
</span><del>- store1_user1 = yield PropertyStore.load("user01", None, self._txn, 10)
</del><ins>+ store1_user1 = yield PropertyStore.load("user01", None, None, self._txn, 10)
</ins><span class="cx"> self.assertTrue("SQL.props:10/user01" in PropertyStore._cacher._memcacheProtocol._cache)
</span><span class="cx">
</span><span class="cx"> pname1 = propertyName("dummy1")
</span><span class="lines">@@ -237,7 +268,7 @@
</span><span class="cx"> # Existing store - add a large property
</span><span class="cx"> self._txn = self.store.newTransaction()
</span><span class="cx"> self.assertFalse("SQL.props:10/user01" in PropertyStore._cacher._memcacheProtocol._cache)
</span><del>- store1_user1 = yield PropertyStore.load("user01", None, self._txn, 10)
</del><ins>+ store1_user1 = yield PropertyStore.load("user01", None, None, self._txn, 10)
</ins><span class="cx"> self.assertTrue("SQL.props:10/user01" in PropertyStore._cacher._memcacheProtocol._cache)
</span><span class="cx">
</span><span class="cx"> pname2 = propertyName("dummy2")
</span><span class="lines">@@ -253,7 +284,7 @@
</span><span class="cx"> # Try again - the cacher will fail large values
</span><span class="cx"> self._txn = self.store.newTransaction()
</span><span class="cx"> self.assertFalse("SQL.props:10/user01" in PropertyStore._cacher._memcacheProtocol._cache)
</span><del>- store1_user1 = yield PropertyStore.load("user01", None, self._txn, 10)
</del><ins>+ store1_user1 = yield PropertyStore.load("user01", None, None, self._txn, 10)
</ins><span class="cx"> self.assertFalse("SQL.props:10/user01" in store1_user1._cacher._memcacheProtocol._cache)
</span><span class="cx">
</span><span class="cx"> self.assertEqual(store1_user1[pname1], pvalue1)
</span><span class="lines">@@ -280,7 +311,7 @@
</span><span class="cx">
</span><span class="cx"> # Existing store - add a normal property
</span><span class="cx"> self.assertFalse("SQL.props:10/user01" in PropertyStore._cacher._memcacheProtocol._cache)
</span><del>- store1_user1 = yield PropertyStore.load("user01", None, self._txn, 10)
</del><ins>+ store1_user1 = yield PropertyStore.load("user01", None, None, self._txn, 10)
</ins><span class="cx"> self.assertFalse("SQL.props:10/user01" in PropertyStore._cacher._memcacheProtocol._cache)
</span><span class="cx">
</span><span class="cx"> pname1 = propertyName("dummy1")
</span><span class="lines">@@ -297,7 +328,7 @@
</span><span class="cx">
</span><span class="cx"> # Existing store - check a normal property
</span><span class="cx"> self.assertFalse("SQL.props:10/user01" in PropertyStore._cacher._memcacheProtocol._cache)
</span><del>- store1_user1 = yield PropertyStore.load("user01", None, self._txn, 10)
</del><ins>+ store1_user1 = yield PropertyStore.load("user01", None, None, self._txn, 10)
</ins><span class="cx"> self.assertFalse("SQL.props:10/user01" in PropertyStore._cacher._memcacheProtocol._cache)
</span><span class="cx"> self.assertEqual(store1_user1[pname1], pvalue1)
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServertrunktxdavbasepropertystoretesttest_xattrpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/base/propertystore/test/test_xattr.py (13619 => 13620)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/base/propertystore/test/test_xattr.py        2014-06-09 16:04:50 UTC (rev 13619)
+++ CalendarServer/trunk/txdav/base/propertystore/test/test_xattr.py        2014-06-09 16:25:14 UTC (rev 13620)
</span><span class="lines">@@ -43,6 +43,12 @@
</span><span class="cx"> self.propertyStore1 = self.propertyStore
</span><span class="cx"> self.propertyStore2 = PropertyStore("user01", lambda : tempFile)
</span><span class="cx"> self.propertyStore2._setPerUserUID("user02")
</span><ins>+ self.propertyStore2._setProxyUID("user02")
+ self.propertyStore3 = PropertyStore("user01", lambda : tempFile)
+ self.propertyStore3._setProxyUID("user03")
+ self.propertyStore4 = PropertyStore("user01", lambda : tempFile)
+ self.propertyStore4._setPerUserUID("user02")
+ self.propertyStore4._setProxyUID("user04")
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def test_init(self):
</span></span></pre></div>
<a id="CalendarServertrunktxdavcaldavdatastorefilepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/caldav/datastore/file.py (13619 => 13620)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/caldav/datastore/file.py        2014-06-09 16:04:50 UTC (rev 13619)
+++ CalendarServer/trunk/txdav/caldav/datastore/file.py        2014-06-09 16:25:14 UTC (rev 13620)
</span><span class="lines">@@ -319,6 +319,7 @@
</span><span class="cx"> PropertyName.fromElement(customxml.GETCTag),
</span><span class="cx"> PropertyName.fromElement(caldavxml.SupportedCalendarComponentSet),
</span><span class="cx"> ),
</span><ins>+ (),
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -748,6 +749,7 @@
</span><span class="cx"> PropertyName.fromElement(customxml.TwistedCalendarHasPrivateCommentsProperty),
</span><span class="cx"> PropertyName.fromElement(customxml.ScheduleChanges),
</span><span class="cx"> ),
</span><ins>+ (),
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServertrunktxdavcaldavdatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/caldav/datastore/sql.py (13619 => 13620)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/caldav/datastore/sql.py        2014-06-09 16:04:50 UTC (rev 13619)
+++ CalendarServer/trunk/txdav/caldav/datastore/sql.py        2014-06-09 16:25:14 UTC (rev 13620)
</span><span class="lines">@@ -48,7 +48,7 @@
</span><span class="cx"> from twisted.python import hashlib
</span><span class="cx"> from twisted.python.failure import Failure
</span><span class="cx">
</span><del>-from twistedcaldav import caldavxml, customxml, ical
</del><ins>+from twistedcaldav import customxml, ical
</ins><span class="cx"> from twistedcaldav.config import config
</span><span class="cx"> from twistedcaldav.datafilters.peruserdata import PerUserDataFilter
</span><span class="cx"> from twistedcaldav.dateops import normalizeForIndex, datetimeMktime, \
</span><span class="lines">@@ -96,6 +96,7 @@
</span><span class="cx"> InvalidResourceMove, InvalidComponentForStoreError, \
</span><span class="cx"> NoSuchObjectResourceError, ConcurrentModification
</span><span class="cx"> from txdav.xml import element
</span><ins>+from txdav.xml.parser import WebDAVDocument
</ins><span class="cx">
</span><span class="cx"> from txdav.idav import ChangeCategory
</span><span class="cx">
</span><span class="lines">@@ -966,6 +967,10 @@
</span><span class="cx">
</span><span class="cx"> _supportedComponents = None
</span><span class="cx">
</span><ins>+ _shadowProperties = tuple([PropertyName.fromString(prop) for prop in config.Sharing.Calendars.CollectionProperties.Shadowable])
+ _proxyProperties = tuple([PropertyName.fromString(prop) for prop in config.Sharing.Calendars.CollectionProperties.ProxyOverride])
+ _globalProperties = tuple([PropertyName.fromString(prop) for prop in config.Sharing.Calendars.CollectionProperties.Global])
+
</ins><span class="cx"> def __init__(self, *args, **kw):
</span><span class="cx"> """
</span><span class="cx"> Initialize a calendar pointing at a record in a database.
</span><span class="lines">@@ -1294,17 +1299,9 @@
</span><span class="cx"> def initPropertyStore(self, props):
</span><span class="cx"> # Setup peruser special properties
</span><span class="cx"> props.setSpecialProperties(
</span><del>- # Shadowable
- (
- PropertyName.fromElement(caldavxml.CalendarDescription),
- PropertyName.fromElement(caldavxml.CalendarTimeZone),
- ),
-
- # Global
- (
- PropertyName.fromElement(customxml.GETCTag),
- PropertyName.fromElement(caldavxml.SupportedCalendarComponentSet),
- ),
</del><ins>+ self._shadowProperties,
+ self._globalProperties,
+ self._proxyProperties,
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -1353,9 +1350,9 @@
</span><span class="cx"> Sub-classes should override to expose the properties they care about.
</span><span class="cx"> """
</span><span class="cx"> props = {}
</span><del>- for elem in (element.DisplayName, caldavxml.CalendarDescription, caldavxml.CalendarTimeZone, customxml.CalendarColor,):
- if PropertyName.fromElement(elem) in self.properties():
- props[elem.sname()] = str(self.properties()[PropertyName.fromElement(elem)])
</del><ins>+ for ename in (PropertyName.fromElement(element.DisplayName),) + self._shadowProperties:
+ if ename in self.properties():
+ props[ename.toString()] = self.properties()[ename].toxml()
</ins><span class="cx"> return props
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -1366,15 +1363,15 @@
</span><span class="cx"> care about.
</span><span class="cx"> """
</span><span class="cx"> # Initialize these for all shares
</span><del>- for elem in (caldavxml.CalendarDescription, caldavxml.CalendarTimeZone,):
- if PropertyName.fromElement(elem) not in self.properties() and elem.sname() in props:
- self.properties()[PropertyName.fromElement(elem)] = elem.fromString(props[elem.sname()])
</del><ins>+ for ename in self._shadowProperties:
+ if ename not in self.properties() and ename.toString() in props:
+ self.properties()[ename] = WebDAVDocument.fromString(props[ename]).root_element
</ins><span class="cx">
</span><span class="cx"> # Only initialize these for direct shares
</span><span class="cx"> if self.direct():
</span><del>- for elem in (element.DisplayName, customxml.CalendarColor,):
- if PropertyName.fromElement(elem) not in self.properties() and elem.sname() in props:
- self.properties()[PropertyName.fromElement(elem)] = elem.fromString(props[elem.sname()])
</del><ins>+ for ename in (PropertyName.fromElement(element.DisplayName),):
+ if ename not in self.properties() and ename.toString() in props:
+ self.properties()[ename] = WebDAVDocument.fromString(props[ename]).root_element
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> # FIXME: this is DAV-ish. Data store calendar objects don't have
</span><span class="lines">@@ -2169,17 +2166,14 @@
</span><span class="cx"> cutype == "RESOURCE" and config.Scheduling.Options.TrackUnscheduledResourceData):
</span><span class="cx">
</span><span class="cx"> # Find current principal and update modified by details
</span><del>- if self._txn._authz_uid is not None:
- authz = yield self.directoryService().recordWithUID(self._txn._authz_uid.decode("utf-8"))
- prop = Property("X-CALENDARSERVER-MODIFIED-BY", authz.canonicalCalendarUserAddress())
- prop.setParameter("CN", authz.displayName)
- for candidate in authz.calendarUserAddresses:
- if candidate.startswith("mailto:"):
- prop.setParameter("EMAIL", candidate[7:])
- break
- component.replacePropertyInAllComponents(prop)
- else:
- component.removeAllPropertiesWithName("X-CALENDARSERVER-MODIFIED-BY")
</del><ins>+ authz = yield self.directoryService().recordWithUID(self.calendar().viewerHome().authzuid().decode("utf-8"))
+ prop = Property("X-CALENDARSERVER-MODIFIED-BY", authz.canonicalCalendarUserAddress())
+ prop.setParameter("CN", authz.displayName)
+ for candidate in authz.calendarUserAddresses:
+ if candidate.startswith("mailto:"):
+ prop.setParameter("EMAIL", candidate[7:])
+ break
+ component.replacePropertyInAllComponents(prop)
</ins><span class="cx"> self._componentChanged = True
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -2197,7 +2191,7 @@
</span><span class="cx">
</span><span class="cx"> # Only DAV:owner is able to set the property to other than PUBLIC
</span><span class="cx"> if internal_state == ComponentUpdateState.NORMAL:
</span><del>- if (self._txn._authz_uid is None or self.calendar().viewerHome().uid() != self._txn._authz_uid) and access != Component.ACCESS_PUBLIC:
</del><ins>+ if (self.calendar().viewerHome().uid() != self.calendar().viewerHome().authzuid()) and access != Component.ACCESS_PUBLIC:
</ins><span class="cx"> raise InvalidCalendarAccessError("Private event access level change not allowed")
</span><span class="cx">
</span><span class="cx"> self.accessMode = access
</span><span class="lines">@@ -3916,13 +3910,16 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def initPropertyStore(self, props):
</span><del>- # Setup peruser special properties
</del><ins>+ # Setup peruser special properties - these are hard-coded for now as clients are not expected
+ # to be using properties on resources - but we do use one special "live" property which we
+ # keep in the propstore.
</ins><span class="cx"> props.setSpecialProperties(
</span><span class="cx"> (
</span><span class="cx"> ),
</span><span class="cx"> (
</span><span class="cx"> PropertyName.fromElement(customxml.ScheduleChanges),
</span><span class="cx"> ),
</span><ins>+ (),
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServertrunktxdavcaldavdatastoretesttest_implicitpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/caldav/datastore/test/test_implicit.py (13619 => 13620)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/caldav/datastore/test/test_implicit.py        2014-06-09 16:04:50 UTC (rev 13619)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/test_implicit.py        2014-06-09 16:25:14 UTC (rev 13620)
</span><span class="lines">@@ -306,18 +306,18 @@
</span><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> self.patch(config, "EnablePrivateEvents", True)
</span><ins>+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user02"
</ins><span class="cx"> calendar_collection = (yield self.calendarUnderTest(home="user01"))
</span><span class="cx"> calendar = Component.fromString(data1)
</span><del>- txn = self.transactionUnderTest()
- txn._authz_uid = "user02"
</del><span class="cx"> yield self.failUnlessFailure(calendar_collection.createCalendarObjectWithName("test.ics", calendar), InvalidCalendarAccessError)
</span><span class="cx"> yield self.commit()
</span><span class="cx">
</span><span class="cx"> # This one should be OK
</span><ins>+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user01"
</ins><span class="cx"> calendar_collection = (yield self.calendarUnderTest(home="user01"))
</span><span class="cx"> calendar = Component.fromString(data1)
</span><del>- txn = self.transactionUnderTest()
- txn._authz_uid = "user01"
</del><span class="cx"> yield calendar_collection.createCalendarObjectWithName("test.ics", calendar)
</span><span class="cx"> yield self.commit()
</span><span class="cx">
</span><span class="lines">@@ -335,10 +335,10 @@
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user01"
</ins><span class="cx"> calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
</span><span class="cx"> calendar = Component.fromString(data2)
</span><del>- txn = self.transactionUnderTest()
- txn._authz_uid = "user01"
</del><span class="cx"> yield calendar_resource.setComponent(calendar)
</span><span class="cx"> yield self.commit()
</span><span class="cx">
</span><span class="lines">@@ -516,10 +516,10 @@
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user01"
</ins><span class="cx"> calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
</span><span class="cx"> calendar = Component.fromString(data2)
</span><del>- txn = self.transactionUnderTest()
- txn._authz_uid = "user01"
</del><span class="cx"> yield calendar_resource.setComponent(calendar)
</span><span class="cx"> yield self.commit()
</span><span class="cx">
</span><span class="lines">@@ -569,10 +569,10 @@
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user01"
</ins><span class="cx"> calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
</span><span class="cx"> calendar = Component.fromString(data2)
</span><del>- txn = self.transactionUnderTest()
- txn._authz_uid = "user01"
</del><span class="cx"> yield calendar_resource.setComponent(calendar)
</span><span class="cx"> yield self.commit()
</span><span class="cx">
</span><span class="lines">@@ -623,10 +623,10 @@
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user01"
</ins><span class="cx"> calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
</span><span class="cx"> calendar = Component.fromString(data2)
</span><del>- txn = self.transactionUnderTest()
- txn._authz_uid = "user01"
</del><span class="cx"> yield calendar_resource.setComponent(calendar)
</span><span class="cx"> yield self.commit()
</span><span class="cx">
</span><span class="lines">@@ -682,10 +682,10 @@
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user01"
</ins><span class="cx"> calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
</span><span class="cx"> calendar = Component.fromString(data2)
</span><del>- txn = self.transactionUnderTest()
- txn._authz_uid = "user01"
</del><span class="cx"> yield calendar_resource.setComponent(calendar)
</span><span class="cx"> yield self.commit()
</span><span class="cx">
</span><span class="lines">@@ -789,10 +789,10 @@
</span><span class="cx"> END:VCALENDAR
</span><span class="cx"> """
</span><span class="cx">
</span><ins>+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user01"
</ins><span class="cx"> calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
</span><span class="cx"> calendar = Component.fromString(data2)
</span><del>- txn = self.transactionUnderTest()
- txn._authz_uid = "user01"
</del><span class="cx"> result = (yield calendar_resource.setComponent(calendar))
</span><span class="cx"> yield self.commit()
</span><span class="cx"> self.assertTrue(result)
</span></span></pre></div>
<a id="CalendarServertrunktxdavcaldavdatastoretesttest_sql_sharingpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/caldav/datastore/test/test_sql_sharing.py (13619 => 13620)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/caldav/datastore/test/test_sql_sharing.py        2014-06-09 16:04:50 UTC (rev 13619)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/test_sql_sharing.py        2014-06-09 16:25:14 UTC (rev 13620)
</span><span class="lines">@@ -23,6 +23,10 @@
</span><span class="cx"> populateCalendarsFrom
</span><span class="cx"> from txdav.common.datastore.sql_tables import _BIND_MODE_READ, \
</span><span class="cx"> _BIND_STATUS_INVITED, _BIND_MODE_DIRECT, _BIND_STATUS_ACCEPTED
</span><ins>+from txdav.base.propertystore.base import PropertyName
+from txdav.xml.base import WebDAVTextElement
+from twistedcaldav import customxml
+from txdav.xml.element import registerElement, registerElementClass
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> class BaseSharingTests(CommonCommonTests, TestCase):
</span><span class="lines">@@ -489,7 +493,94 @@
</span><span class="cx"> yield self.commit()
</span><span class="cx">
</span><span class="cx">
</span><ins>+ @inlineCallbacks
+ def test_perUserSharedProxyCollectionProperties(self):
+ """
+ Test that sharees and proxies get their own per-user properties, with some being
+ initialized based ont he owner value.
+ """
+ @registerElement
+ @registerElementClass
+ class DummySharingProperty (WebDAVTextElement):
+ namespace = "http://calendarserver.org/ns/"
+ name = "dummy-sharing"
</ins><span class="cx">
</span><ins>+ shared_name = yield self._createShare()
+
+ # Add owner properties
+ home = yield self.homeUnderTest(name="user01")
+ calendar = yield home.calendarWithName("calendar")
+
+ calendar.properties()[PropertyName.fromElement(DummySharingProperty)] = DummySharingProperty.fromString("user01")
+ calendar.properties()[PropertyName.fromElement(customxml.CalendarColor)] = customxml.CalendarColor.fromString("#000001")
+ yield self.commit()
+
+ # Check/add sharee properties
+ home = yield self.homeUnderTest(name="user02")
+ calendar = yield home.calendarWithName(shared_name)
+ self.assertTrue(PropertyName.fromElement(DummySharingProperty) not in calendar.properties())
+ self.assertTrue(PropertyName.fromElement(customxml.CalendarColor) not in calendar.properties())
+ calendar.properties()[PropertyName.fromElement(DummySharingProperty)] = DummySharingProperty.fromString("user02")
+ calendar.properties()[PropertyName.fromElement(customxml.CalendarColor)] = customxml.CalendarColor.fromString("#000002")
+ yield self.commit()
+
+ # Check/add owner proxy properties
+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user03"
+ home = yield self.homeUnderTest(name="user01")
+ calendar = yield home.calendarWithName("calendar")
+ self.assertTrue(PropertyName.fromElement(DummySharingProperty) in calendar.properties())
+ self.assertEqual(str(calendar.properties()[PropertyName.fromElement(DummySharingProperty)]), "user01")
+ self.assertTrue(PropertyName.fromElement(customxml.CalendarColor) in calendar.properties())
+ self.assertEqual(str(calendar.properties()[PropertyName.fromElement(customxml.CalendarColor)]), "#000001")
+ calendar.properties()[PropertyName.fromElement(DummySharingProperty)] = DummySharingProperty.fromString("user03")
+ calendar.properties()[PropertyName.fromElement(customxml.CalendarColor)] = customxml.CalendarColor.fromString("#000003")
+ yield self.commit()
+
+ # Check/add sharee proxy properties
+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user04"
+ home = yield self.homeUnderTest(name="user02")
+ calendar = yield home.calendarWithName(shared_name)
+ self.assertTrue(PropertyName.fromElement(DummySharingProperty) in calendar.properties())
+ self.assertEqual(str(calendar.properties()[PropertyName.fromElement(DummySharingProperty)]), "user02")
+ self.assertTrue(PropertyName.fromElement(customxml.CalendarColor) in calendar.properties())
+ self.assertEqual(str(calendar.properties()[PropertyName.fromElement(customxml.CalendarColor)]), "#000002")
+ calendar.properties()[PropertyName.fromElement(DummySharingProperty)] = DummySharingProperty.fromString("user04")
+ calendar.properties()[PropertyName.fromElement(customxml.CalendarColor)] = customxml.CalendarColor.fromString("#000004")
+ yield self.commit()
+
+ # Validate all properties
+ home = yield self.homeUnderTest(name="user01")
+ calendar = yield home.calendarWithName("calendar")
+ self.assertEqual(str(calendar.properties()[PropertyName.fromElement(DummySharingProperty)]), "user03")
+ self.assertEqual(str(calendar.properties()[PropertyName.fromElement(customxml.CalendarColor)]), "#000001")
+ yield self.commit()
+
+ home = yield self.homeUnderTest(name="user02")
+ calendar = yield home.calendarWithName(shared_name)
+ self.assertEqual(str(calendar.properties()[PropertyName.fromElement(DummySharingProperty)]), "user04")
+ self.assertEqual(str(calendar.properties()[PropertyName.fromElement(customxml.CalendarColor)]), "#000002")
+ yield self.commit()
+
+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user03"
+ home = yield self.homeUnderTest(name="user01")
+ calendar = yield home.calendarWithName("calendar")
+ self.assertEqual(str(calendar.properties()[PropertyName.fromElement(DummySharingProperty)]), "user03")
+ self.assertEqual(str(calendar.properties()[PropertyName.fromElement(customxml.CalendarColor)]), "#000003")
+ yield self.commit()
+
+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user04"
+ home = yield self.homeUnderTest(name="user02")
+ calendar = yield home.calendarWithName(shared_name)
+ self.assertEqual(str(calendar.properties()[PropertyName.fromElement(DummySharingProperty)]), "user04")
+ self.assertEqual(str(calendar.properties()[PropertyName.fromElement(customxml.CalendarColor)]), "#000004")
+ yield self.commit()
+
+
+
</ins><span class="cx"> class SharingRevisions(BaseSharingTests):
</span><span class="cx"> """
</span><span class="cx"> Test store-based sharing and interaction with revision table.
</span></span></pre></div>
<a id="CalendarServertrunktxdavcarddavdatastorefilepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/carddav/datastore/file.py (13619 => 13620)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/carddav/datastore/file.py        2014-06-09 16:04:50 UTC (rev 13619)
+++ CalendarServer/trunk/txdav/carddav/datastore/file.py        2014-06-09 16:25:14 UTC (rev 13620)
</span><span class="lines">@@ -139,6 +139,7 @@
</span><span class="cx"> (
</span><span class="cx"> PropertyName.fromElement(customxml.GETCTag),
</span><span class="cx"> ),
</span><ins>+ (),
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServertrunktxdavcarddavdatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/carddav/datastore/sql.py (13619 => 13620)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/carddav/datastore/sql.py        2014-06-09 16:04:50 UTC (rev 13619)
+++ CalendarServer/trunk/txdav/carddav/datastore/sql.py        2014-06-09 16:25:14 UTC (rev 13620)
</span><span class="lines">@@ -39,7 +39,6 @@
</span><span class="cx"> from twisted.internet.defer import inlineCallbacks, returnValue
</span><span class="cx"> from twisted.python import hashlib
</span><span class="cx">
</span><del>-from twistedcaldav import carddavxml, customxml
</del><span class="cx"> from twistedcaldav.config import config
</span><span class="cx"> from twistedcaldav.memcacher import Memcacher
</span><span class="cx"> from twistedcaldav.vcard import Component as VCard, InvalidVCardDataError, Property, \
</span><span class="lines">@@ -65,6 +64,7 @@
</span><span class="cx"> AllRetriesFailed, ObjectResourceNameAlreadyExistsError, \
</span><span class="cx"> SyncTokenValidException, IndexedSearchException
</span><span class="cx"> from txdav.xml import element
</span><ins>+from txdav.xml.parser import WebDAVDocument
</ins><span class="cx">
</span><span class="cx"> from zope.interface.declarations import implements
</span><span class="cx">
</span><span class="lines">@@ -87,9 +87,9 @@
</span><span class="cx"> _cacher = Memcacher("SQL.adbkhome", pickle=True, key_normalization=False)
</span><span class="cx">
</span><span class="cx">
</span><del>- def __init__(self, transaction, ownerUID):
</del><ins>+ def __init__(self, transaction, ownerUID, authzUID=None):
</ins><span class="cx">
</span><del>- super(AddressBookHome, self).__init__(transaction, ownerUID)
</del><ins>+ super(AddressBookHome, self).__init__(transaction, ownerUID, authzUID=authzUID)
</ins><span class="cx"> self._addressbookPropertyStoreID = None
</span><span class="cx"> self._addressbook = None
</span><span class="cx">
</span><span class="lines">@@ -467,6 +467,9 @@
</span><span class="cx"> "UID": _objectSchema.UID,
</span><span class="cx"> }
</span><span class="cx">
</span><ins>+ _shadowProperties = tuple([PropertyName.fromString(prop) for prop in config.Sharing.AddressBooks.CollectionProperties.Shadowable])
+ _proxyProperties = tuple([PropertyName.fromString(prop) for prop in config.Sharing.AddressBooks.CollectionProperties.ProxyOverride])
+ _globalProperties = tuple([PropertyName.fromString(prop) for prop in config.Sharing.AddressBooks.CollectionProperties.Global])
</ins><span class="cx">
</span><span class="cx"> @classmethod
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -797,6 +800,7 @@
</span><span class="cx"> props = yield PropertyStore.load(
</span><span class="cx"> self.ownerHome().uid(),
</span><span class="cx"> self.viewerHome().uid(),
</span><ins>+ None,
</ins><span class="cx"> self._txn,
</span><span class="cx"> self.ownerHome()._addressbookPropertyStoreID, # not ._resourceID as in CommonHomeChild._loadPropertyStore()
</span><span class="cx"> notifyCallback=self.notifyPropertyChanged
</span><span class="lines">@@ -807,25 +811,22 @@
</span><span class="cx"> def initPropertyStore(self, props):
</span><span class="cx"> # Setup peruser special properties
</span><span class="cx"> props.setSpecialProperties(
</span><del>- (
- PropertyName.fromElement(carddavxml.AddressBookDescription),
- ),
- (
- PropertyName.fromElement(customxml.GETCTag),
- ),
</del><ins>+ self._shadowProperties,
+ self._globalProperties,
+ self._proxyProperties,
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> def getInviteCopyProperties(self):
</span><span class="cx"> """
</span><del>- Get a dictionary of property name/values (as strings) for properties that are shadowable and
</del><ins>+ Get a dictionary of property name/values (as XML strings) for properties that are shadowable and
</ins><span class="cx"> need to be copied to a sharee's collection when an external (cross-pod) share is created.
</span><span class="cx"> Sub-classes should override to expose the properties they care about.
</span><span class="cx"> """
</span><span class="cx"> props = {}
</span><del>- for elem in (element.DisplayName, carddavxml.AddressBookDescription,):
- if PropertyName.fromElement(elem) in self.properties():
- props[elem.sname()] = str(self.properties()[PropertyName.fromElement(elem)])
</del><ins>+ for ename in (PropertyName.fromElement(element.DisplayName),) + self._shadowProperties:
+ if ename in self.properties():
+ props[ename.toString()] = self.properties()[ename].toxml()
</ins><span class="cx"> return props
</span><span class="cx">
</span><span class="cx">
</span><span class="lines">@@ -836,15 +837,15 @@
</span><span class="cx"> care about.
</span><span class="cx"> """
</span><span class="cx"> # Initialize these for all shares
</span><del>- for elem in (carddavxml.AddressBookDescription,):
- if PropertyName.fromElement(elem) not in self.properties() and elem.sname() in props:
- self.properties()[PropertyName.fromElement(elem)] = elem.fromString(props[elem.sname()])
</del><ins>+ for ename in self._shadowProperties:
+ if ename not in self.properties() and ename.toString() in props:
+ self.properties()[ename] = WebDAVDocument.fromString(props[ename]).root_element
</ins><span class="cx">
</span><span class="cx"> # Only initialize these for direct shares
</span><span class="cx"> if self.direct():
</span><del>- for elem in (element.DisplayName,):
- if PropertyName.fromElement(elem) not in self.properties() and elem.sname() in props:
- self.properties()[PropertyName.fromElement(elem)] = elem.fromString(props[elem.sname()])
</del><ins>+ for ename in (PropertyName.fromElement(element.DisplayName),):
+ if ename not in self.properties() and ename.toString() in props:
+ self.properties()[ename] = WebDAVDocument.fromString(props[ename]).root_element
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> def contentType(self):
</span><span class="lines">@@ -1167,7 +1168,7 @@
</span><span class="cx"> # Get property stores for all these child resources (if any found)
</span><span class="cx"> addressbookPropertyStoreIDs = [ownerHomeItem._addressbookPropertyStoreID for ownerHomeItem in ownerHomeToDataRowMap]
</span><span class="cx"> propertyStores = yield PropertyStore.forMultipleResourcesWithResourceIDs(
</span><del>- home.uid(), home._txn, addressbookPropertyStoreIDs
</del><ins>+ home.uid(), None, None, home._txn, addressbookPropertyStoreIDs
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> addressbookResourceIDs = [ownerHomeItem.addressbook()._resourceID for ownerHomeItem in ownerHomeToDataRowMap]
</span></span></pre></div>
<a id="CalendarServertrunktxdavcommondatastorefilepy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/common/datastore/file.py (13619 => 13620)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/common/datastore/file.py        2014-06-09 16:04:50 UTC (rev 13619)
+++ CalendarServer/trunk/txdav/common/datastore/file.py        2014-06-09 16:25:14 UTC (rev 13620)
</span><span class="lines">@@ -1701,6 +1701,7 @@
</span><span class="cx"> (
</span><span class="cx"> PropertyName.fromElement(customxml.NotificationType),
</span><span class="cx"> ),
</span><ins>+ (),
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx">
</span></span></pre></div>
<a id="CalendarServertrunktxdavcommondatastoresqlpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/common/datastore/sql.py (13619 => 13620)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/common/datastore/sql.py        2014-06-09 16:04:50 UTC (rev 13619)
+++ CalendarServer/trunk/txdav/common/datastore/sql.py        2014-06-09 16:25:14 UTC (rev 13620)
</span><span class="lines">@@ -694,7 +694,7 @@
</span><span class="cx"> ).on(self)
</span><span class="cx">
</span><span class="cx">
</span><del>- def _determineMemo(self, storeType, uid, create=False):
</del><ins>+ def _determineMemo(self, storeType, uid, create=False, authzUID=None):
</ins><span class="cx"> """
</span><span class="cx"> Determine the memo dictionary to use for homeWithUID.
</span><span class="cx"> """
</span><span class="lines">@@ -720,19 +720,27 @@
</span><span class="cx">
</span><span class="cx">
</span><span class="cx"> @memoizedKey("uid", _determineMemo)
</span><del>- def homeWithUID(self, storeType, uid, create=False):
</del><ins>+ def homeWithUID(self, storeType, uid, create=False, authzUID=None):
+ """
+ We need to distinguish between various different users "looking" at a home and its
+ child resources because we have per-user properties that depend on which user is "looking".
+ By default the viewer is set to the authz_uid on the transaction, or the owner if no authz,
+ but it can be overridden using L{authzUID}. This is useful when the store needs to get to
+ other user's homes with the viewer being the owner of that home as opposed to authz_uid. That
+ often happens when manipulating shares.
+ """
</ins><span class="cx"> if storeType not in (ECALENDARTYPE, EADDRESSBOOKTYPE):
</span><span class="cx"> raise RuntimeError("Unknown home type.")
</span><span class="cx">
</span><del>- return self._homeClass[storeType].homeWithUID(self, uid, create)
</del><ins>+ return self._homeClass[storeType].homeWithUID(self, uid, create, authzUID)
</ins><span class="cx">
</span><span class="cx">
</span><del>- def calendarHomeWithUID(self, uid, create=False):
- return self.homeWithUID(ECALENDARTYPE, uid, create=create)
</del><ins>+ def calendarHomeWithUID(self, uid, create=False, authzUID=None):
+ return self.homeWithUID(ECALENDARTYPE, uid, create=create, authzUID=authzUID)
</ins><span class="cx">
</span><span class="cx">
</span><del>- def addressbookHomeWithUID(self, uid, create=False):
- return self.homeWithUID(EADDRESSBOOKTYPE, uid, create=create)
</del><ins>+ def addressbookHomeWithUID(self, uid, create=False, authzUID=None):
+ return self.homeWithUID(EADDRESSBOOKTYPE, uid, create=create, authzUID=authzUID)
</ins><span class="cx">
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="lines">@@ -740,10 +748,10 @@
</span><span class="cx"> """
</span><span class="cx"> Load a calendar or addressbook home by its integer resource ID.
</span><span class="cx"> """
</span><del>- uid = (yield self._homeClass[storeType]
- .homeUIDWithResourceID(self, rid))
</del><ins>+ uid = (yield self._homeClass[storeType].homeUIDWithResourceID(self, rid))
</ins><span class="cx"> if uid:
</span><del>- result = (yield self.homeWithUID(storeType, uid))
</del><ins>+ # Always get the owner's view of the home = i.e., authzUID=uid
+ result = (yield self.homeWithUID(storeType, uid, authzUID=uid))
</ins><span class="cx"> else:
</span><span class="cx"> result = None
</span><span class="cx"> returnValue(result)
</span><span class="lines">@@ -2838,7 +2846,7 @@
</span><span class="cx">
</span><span class="cx"> @classmethod
</span><span class="cx"> @inlineCallbacks
</span><del>- def makeClass(cls, transaction, ownerUID, no_cache=False):
</del><ins>+ def makeClass(cls, transaction, ownerUID, no_cache=False, authzUID=None):
</ins><span class="cx"> """
</span><span class="cx"> Build the actual home class taking into account the possibility that we might need to
</span><span class="cx"> switch in the external version of the class.
</span><span class="lines">@@ -2850,14 +2858,20 @@
</span><span class="cx"> @param no_cache: should cached query be used
</span><span class="cx"> @type no_cache: C{bool}
</span><span class="cx"> """
</span><del>- home = cls(transaction, ownerUID)
</del><ins>+ home = cls(transaction, ownerUID, authzUID=authzUID)
</ins><span class="cx"> actualHome = yield home.initFromStore(no_cache)
</span><span class="cx"> returnValue(actualHome)
</span><span class="cx">
</span><span class="cx">
</span><del>- def __init__(self, transaction, ownerUID):
</del><ins>+ def __init__(self, transaction, ownerUID, authzUID=None):
</ins><span class="cx"> self._txn = transaction
</span><span class="cx"> self._ownerUID = ownerUID
</span><ins>+ self._authzUID = authzUID
+ if self._authzUID is None:
+ if self._txn._authz_uid is not None:
+ self._authzUID = self._txn._authz_uid
+ else:
+ self._authzUID = self._ownerUID
</ins><span class="cx"> self._resourceID = None
</span><span class="cx"> self._status = _HOME_STATUS_NORMAL
</span><span class="cx"> self._dataVersion = None
</span><span class="lines">@@ -3052,11 +3066,11 @@
</span><span class="cx">
</span><span class="cx"> @classmethod
</span><span class="cx"> @inlineCallbacks
</span><del>- def homeWithUID(cls, txn, uid, create=False):
</del><ins>+ def homeWithUID(cls, txn, uid, create=False, authzUID=None):
</ins><span class="cx"> """
</span><span class="cx"> @param uid: I'm going to assume uid is utf-8 encoded bytes
</span><span class="cx"> """
</span><del>- homeObject = yield cls.makeClass(txn, uid)
</del><ins>+ homeObject = yield cls.makeClass(txn, uid, authzUID=authzUID)
</ins><span class="cx"> if homeObject is not None:
</span><span class="cx"> returnValue(homeObject)
</span><span class="cx"> else:
</span><span class="lines">@@ -3091,7 +3105,7 @@
</span><span class="cx"> yield savepoint.rollback(txn)
</span><span class="cx">
</span><span class="cx"> # Retry the query - row may exist now, if not re-raise
</span><del>- homeObject = yield cls.makeClass(txn, uid)
</del><ins>+ homeObject = yield cls.makeClass(txn, uid, authzUID=authzUID)
</ins><span class="cx"> if homeObject:
</span><span class="cx"> returnValue(homeObject)
</span><span class="cx"> else:
</span><span class="lines">@@ -3102,7 +3116,7 @@
</span><span class="cx"> # Note that we must not cache the owner_uid->resource_id
</span><span class="cx"> # mapping in _cacher when creating as we don't want that to appear
</span><span class="cx"> # until AFTER the commit
</span><del>- home = yield cls.makeClass(txn, uid, no_cache=True)
</del><ins>+ home = yield cls.makeClass(txn, uid, no_cache=True, authzUID=authzUID)
</ins><span class="cx"> yield home.createdHome()
</span><span class="cx"> returnValue(home)
</span><span class="cx">
</span><span class="lines">@@ -3140,6 +3154,15 @@
</span><span class="cx"> return self._ownerUID
</span><span class="cx">
</span><span class="cx">
</span><ins>+ def authzuid(self):
+ """
+ Retrieve the unique identifier of the user accessing the data in this home.
+
+ @return: a string.
+ """
+ return self._authzUID
+
+
</ins><span class="cx"> def external(self):
</span><span class="cx"> """
</span><span class="cx"> Is this an external home.
</span><span class="lines">@@ -3540,9 +3563,13 @@
</span><span class="cx">
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _loadPropertyStore(self):
</span><ins>+
+ # Use any authz uid in place of the viewer uid so delegates have their own
+ # set of properties
</ins><span class="cx"> props = yield PropertyStore.load(
</span><span class="cx"> self.uid(),
</span><span class="cx"> self.uid(),
</span><ins>+ self.authzuid(),
</ins><span class="cx"> self._txn,
</span><span class="cx"> self._resourceID,
</span><span class="cx"> notifyCallback=self.notifyChanged
</span><span class="lines">@@ -4652,6 +4679,9 @@
</span><span class="cx"> def ownerView(self):
</span><span class="cx"> """
</span><span class="cx"> Return the owner resource counterpart of this shared resource.
</span><ins>+
+ Note we have to play a trick with the property store to coerce it to match
+ the per-user properties for the owner.
</ins><span class="cx"> """
</span><span class="cx"> # Get the child of the owner home that has the same resource id as the owned one
</span><span class="cx"> ownerView = yield self.ownerHome().childWithID(self.id())
</span><span class="lines">@@ -4662,10 +4692,13 @@
</span><span class="cx"> def shareeView(self, shareeUID):
</span><span class="cx"> """
</span><span class="cx"> Return the shared resource counterpart of this owned resource for the specified sharee.
</span><ins>+
+ Note we have to play a trick with the property store to coerce it to match
+ the per-user properties for the sharee.
</ins><span class="cx"> """
</span><span class="cx">
</span><span class="cx"> # Get the child of the sharee home that has the same resource id as the owned one
</span><del>- shareeHome = yield self._txn.homeWithUID(self._home._homeType, shareeUID)
</del><ins>+ shareeHome = yield self._txn.homeWithUID(self._home._homeType, shareeUID, authzUID=shareeUID)
</ins><span class="cx"> shareeView = (yield shareeHome.allChildWithID(self.id())) if shareeHome is not None else None
</span><span class="cx"> returnValue(shareeView)
</span><span class="cx">
</span><span class="lines">@@ -5434,7 +5467,7 @@
</span><span class="cx"> childResourceIDs = [dataRow[2] for dataRow in dataRows]
</span><span class="cx">
</span><span class="cx"> propertyStores = yield PropertyStore.forMultipleResourcesWithResourceIDs(
</span><del>- home.uid(), home._txn, childResourceIDs
</del><ins>+ home.uid(), None, None, home._txn, childResourceIDs
</ins><span class="cx"> )
</span><span class="cx">
</span><span class="cx"> # Get revisions
</span><span class="lines">@@ -6216,9 +6249,12 @@
</span><span class="cx"> @inlineCallbacks
</span><span class="cx"> def _loadPropertyStore(self, props=None):
</span><span class="cx"> if props is None:
</span><ins>+ # Use any authz uid in place of the viewer uid so delegates have their own
+ # set of properties
</ins><span class="cx"> props = yield PropertyStore.load(
</span><span class="cx"> self.ownerHome().uid(),
</span><span class="cx"> self.viewerHome().uid(),
</span><ins>+ self.viewerHome().authzuid(),
</ins><span class="cx"> self._txn,
</span><span class="cx"> self._resourceID,
</span><span class="cx"> notifyCallback=self.notifyPropertyChanged
</span><span class="lines">@@ -6534,6 +6570,8 @@
</span><span class="cx"> if parent.objectResourcesHaveProperties():
</span><span class="cx"> propertyStores = (yield PropertyStore.forMultipleResources(
</span><span class="cx"> parent._home.uid(),
</span><ins>+ None,
+ None,
</ins><span class="cx"> parent._txn,
</span><span class="cx"> cls._objectSchema.RESOURCE_ID,
</span><span class="cx"> cls._objectSchema.PARENT_RESOURCE_ID,
</span><span class="lines">@@ -6611,6 +6649,8 @@
</span><span class="cx"> if parent.objectResourcesHaveProperties():
</span><span class="cx"> propertyStores = (yield PropertyStore.forMultipleResourcesWithResourceIDs(
</span><span class="cx"> parent._home.uid(),
</span><ins>+ None,
+ None,
</ins><span class="cx"> parent._txn,
</span><span class="cx"> tuple([row[0] for row in dataRows]),
</span><span class="cx"> ))
</span><span class="lines">@@ -6789,8 +6829,9 @@
</span><span class="cx"> if props is None:
</span><span class="cx"> if self._parentCollection.objectResourcesHaveProperties():
</span><span class="cx"> props = yield PropertyStore.load(
</span><ins>+ self._parentCollection.ownerHome().uid(),
</ins><span class="cx"> self._parentCollection.viewerHome().uid(),
</span><del>- self._parentCollection.ownerHome().uid(),
</del><ins>+ self._parentCollection.viewerHome().authzuid(),
</ins><span class="cx"> self._txn,
</span><span class="cx"> self._resourceID,
</span><span class="cx"> created=created
</span><span class="lines">@@ -7153,6 +7194,7 @@
</span><span class="cx"> self._propertyStore = yield PropertyStore.load(
</span><span class="cx"> self._uid,
</span><span class="cx"> self._uid,
</span><ins>+ None,
</ins><span class="cx"> self._txn,
</span><span class="cx"> self._resourceID,
</span><span class="cx"> notifyCallback=self.notifyChanged
</span><span class="lines">@@ -7492,6 +7534,8 @@
</span><span class="cx"> # Get property stores for all these child resources (if any found)
</span><span class="cx"> propertyStores = (yield PropertyStore.forMultipleResources(
</span><span class="cx"> parent.uid(),
</span><ins>+ None,
+ None,
</ins><span class="cx"> parent._txn,
</span><span class="cx"> schema.NOTIFICATION.RESOURCE_ID,
</span><span class="cx"> schema.NOTIFICATION.NOTIFICATION_HOME_RESOURCE_ID,
</span></span></pre></div>
<a id="CalendarServertrunktxdavcommondatastoresql_externalpy"></a>
<div class="modfile"><h4>Modified: CalendarServer/trunk/txdav/common/datastore/sql_external.py (13619 => 13620)</h4>
<pre class="diff"><span>
<span class="info">--- CalendarServer/trunk/txdav/common/datastore/sql_external.py        2014-06-09 16:04:50 UTC (rev 13619)
+++ CalendarServer/trunk/txdav/common/datastore/sql_external.py        2014-06-09 16:25:14 UTC (rev 13620)
</span><span class="lines">@@ -149,6 +149,7 @@
</span><span class="cx"> props = yield PropertyStore.load(
</span><span class="cx"> self.uid(),
</span><span class="cx"> self.uid(),
</span><ins>+ None,
</ins><span class="cx"> self._txn,
</span><span class="cx"> self._resourceID,
</span><span class="cx"> notifyCallback=self.notifyChanged
</span></span></pre>
</div>
</div>
</body>
</html>