[CalendarServer-changes] [13620] CalendarServer/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Mon Jun 9 09:25:14 PDT 2014
Revision: 13620
http://trac.calendarserver.org//changeset/13620
Author: cdaboo at apple.com
Date: 2014-06-09 09:25:14 -0700 (Mon, 09 Jun 2014)
Log Message:
-----------
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.
Modified Paths:
--------------
CalendarServer/trunk/calendarserver/provision/root.py
CalendarServer/trunk/calendarserver/tap/caldav.py
CalendarServer/trunk/calendarserver/tools/cmdline.py
CalendarServer/trunk/calendarserver/tools/test/test_gateway.py
CalendarServer/trunk/calendarserver/tools/test/test_principals.py
CalendarServer/trunk/twistedcaldav/directory/util.py
CalendarServer/trunk/twistedcaldav/resource.py
CalendarServer/trunk/twistedcaldav/scheduling_store/caldav/resource.py
CalendarServer/trunk/twistedcaldav/sharing.py
CalendarServer/trunk/twistedcaldav/stdconfig.py
CalendarServer/trunk/twistedcaldav/storebridge.py
CalendarServer/trunk/txdav/base/propertystore/base.py
CalendarServer/trunk/txdav/base/propertystore/sql.py
CalendarServer/trunk/txdav/base/propertystore/test/base.py
CalendarServer/trunk/txdav/base/propertystore/test/test_sql.py
CalendarServer/trunk/txdav/base/propertystore/test/test_xattr.py
CalendarServer/trunk/txdav/caldav/datastore/file.py
CalendarServer/trunk/txdav/caldav/datastore/sql.py
CalendarServer/trunk/txdav/caldav/datastore/test/test_implicit.py
CalendarServer/trunk/txdav/caldav/datastore/test/test_sql_sharing.py
CalendarServer/trunk/txdav/carddav/datastore/file.py
CalendarServer/trunk/txdav/carddav/datastore/sql.py
CalendarServer/trunk/txdav/common/datastore/file.py
CalendarServer/trunk/txdav/common/datastore/sql.py
CalendarServer/trunk/txdav/common/datastore/sql_external.py
Modified: CalendarServer/trunk/calendarserver/provision/root.py
===================================================================
--- 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)
@@ -364,12 +364,7 @@
.format(agent)
))
- if (
- config.EnableResponseCache and
- request.method == "PROPFIND" and
- not getattr(request, "notInCache", False) and
- len(segments) > 1
- ):
+ if not hasattr(request, "authzUser"):
try:
authnUser, authzUser = yield self.authenticate(request)
request.authnUser = authnUser
@@ -381,6 +376,13 @@
)
raise HTTPError(response)
+ if (
+ config.EnableResponseCache and
+ request.method == "PROPFIND" and
+ not getattr(request, "notInCache", False) and
+ len(segments) > 1
+ ):
+
try:
if not getattr(request, "checkingCache", False):
request.checkingCache = True
Modified: CalendarServer/trunk/calendarserver/tap/caldav.py
===================================================================
--- 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)
@@ -38,6 +38,8 @@
from zope.interface import implements
+from twistedcaldav.stdconfig import config
+
from twisted.python.log import FileLogObserver, ILogObserver
from twisted.python.logfile import LogFile
from twisted.python.usage import Options, UsageError
@@ -96,7 +98,7 @@
from txdav.who.groups import GroupCacher
from twistedcaldav import memcachepool
-from twistedcaldav.config import config, ConfigurationError
+from twistedcaldav.config import ConfigurationError
from twistedcaldav.localization import processLocalizationFiles
from twistedcaldav.stdconfig import DEFAULT_CONFIG, DEFAULT_CONFIG_FILE
from twistedcaldav.upgrade import (
Modified: CalendarServer/trunk/calendarserver/tools/cmdline.py
===================================================================
--- 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)
@@ -18,6 +18,7 @@
Shared main-point between utilities.
"""
+from twistedcaldav.stdconfig import config
from calendarserver.tap.caldav import CalDAVServiceMaker, CalDAVOptions
from calendarserver.tap.util import checkDirectories
from calendarserver.tools.util import loadConfig, autoDisableMemcached
Modified: CalendarServer/trunk/calendarserver/tools/test/test_gateway.py
===================================================================
--- 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)
@@ -20,12 +20,12 @@
import plistlib
import xml
+from twistedcaldav.stdconfig import config
from twext.python.filepath import CachingFilePath as FilePath
from twisted.internet import reactor
from twisted.internet.defer import inlineCallbacks, Deferred, returnValue
from twisted.trial.unittest import TestCase
from twistedcaldav import memcacher
-from twistedcaldav.config import config
from twistedcaldav.memcacheclient import ClientFactory
from twistedcaldav.test.util import CapturingProcessProtocol
from txdav.common.datastore.test.util import (
Modified: CalendarServer/trunk/calendarserver/tools/test/test_principals.py
===================================================================
--- 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)
@@ -16,6 +16,7 @@
import os
+from twistedcaldav.stdconfig import config
from calendarserver.tools.principals import (
parseCreationArgs, matchStrings,
recordForPrincipalID, getProxies, setProxies
@@ -23,7 +24,6 @@
from twext.python.filepath import CachingFilePath as FilePath
from twisted.internet import reactor
from twisted.internet.defer import inlineCallbacks, Deferred, returnValue
-from twistedcaldav.config import config
from twistedcaldav.test.util import (
TestCase, StoreTestCase, CapturingProcessProtocol, ErrorOutput
)
Modified: CalendarServer/trunk/twistedcaldav/directory/util.py
===================================================================
--- 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)
@@ -92,7 +92,11 @@
"""
transaction = getattr(request, TRANSACTION_KEY, None)
if transaction is None:
- transaction = newStore.newTransaction(repr(request))
+ 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)
def abortIfUncommitted(request, response):
try:
# TODO: missing 'yield' here. For formal correctness as per
Modified: CalendarServer/trunk/twistedcaldav/resource.py
===================================================================
--- 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)
@@ -2635,11 +2635,16 @@
if config.EnableProxyPrincipals:
# Server may be read only
if config.EnableReadOnlyServer:
- rw_proxy_privs = (
+ ro_proxy_privs = rw_proxy_privs = (
element.Privilege(element.Read()),
element.Privilege(element.ReadCurrentUserPrivilegeSet()),
)
else:
+ ro_proxy_privs = (
+ element.Privilege(element.Read()),
+ element.Privilege(element.ReadCurrentUserPrivilegeSet()),
+ element.Privilege(element.WriteProperties()),
+ )
rw_proxy_privs = (
element.Privilege(element.Read()),
element.Privilege(element.ReadCurrentUserPrivilegeSet()),
@@ -2650,10 +2655,7 @@
# DAV:read/DAV:read-current-user-privilege-set access for this principal's calendar-proxy-read users.
element.ACE(
element.Principal(element.HRef(joinURL(myPrincipal.principalURL(), "calendar-proxy-read/"))),
- element.Grant(
- element.Privilege(element.Read()),
- element.Privilege(element.ReadCurrentUserPrivilegeSet()),
- ),
+ element.Grant(*ro_proxy_privs),
element.Protected(),
TwistedACLInheritable(),
),
Modified: CalendarServer/trunk/twistedcaldav/scheduling_store/caldav/resource.py
===================================================================
--- 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)
@@ -419,17 +419,6 @@
originator = (yield self.loadOriginatorFromRequestDetails(request))
recipients = self.loadRecipientsFromCalendarData(calendar)
- # 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
-
# Log extended item
if not hasattr(request, "extendedLogItems"):
request.extendedLogItems = {}
Modified: CalendarServer/trunk/twistedcaldav/sharing.py
===================================================================
--- 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)
@@ -383,6 +383,7 @@
element.Grant(
element.Privilege(element.Read()),
element.Privilege(element.ReadCurrentUserPrivilegeSet()),
+ element.Privilege(element.WriteProperties()),
),
element.Protected(),
TwistedACLInheritable(),
Modified: CalendarServer/trunk/twistedcaldav/stdconfig.py
===================================================================
--- 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)
@@ -606,9 +606,31 @@
"IgnorePerUserProperties" : [
"X-APPLE-STRUCTURED-LOCATION",
],
+ "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": [
+ ],
+ },
},
"AddressBooks" : {
"Enabled" : False, # Address Books on/off switch
+ "CollectionProperties": {
+ "Shadowable": [
+ "{urn:ietf:params:xml:ns:carddav}addressbook-description",
+ ],
+ "ProxyOverride": [
+ ],
+ "Global": [
+ ],
+ },
},
},
Modified: CalendarServer/trunk/twistedcaldav/storebridge.py
===================================================================
--- 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)
@@ -2882,17 +2882,6 @@
"Can't parse calendar data: %s" % (str(e),)
))
- # 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
-
try:
response = (yield self.storeComponent(component, smart_merge=schedule_tag_match))
except ResourceDeletedError:
@@ -3623,17 +3612,6 @@
"Could not parse vCard",
))
- # 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
-
try:
response = (yield self.storeComponent(component))
except ResourceDeletedError:
Modified: CalendarServer/trunk/txdav/base/propertystore/base.py
===================================================================
--- 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)
@@ -26,6 +26,7 @@
from twext.python.log import Logger
from txdav.xml import element as davxml
from txdav.xml.base import encodeXMLName
+from txdav.xml.element import lookupElement
from txweb2.dav.resource import TwistedGETContentMD5, \
TwistedQuotaRootProperty
@@ -98,7 +99,11 @@
return encodeXMLName(self.namespace, self.name)
+ def toElement(self):
+ return lookupElement((self.namespace, self.name,))
+
+
class AbstractPropertyStore(DictMixin, object):
"""
Base property store.
@@ -108,6 +113,7 @@
implements(IPropertyStore)
_defaultShadowableKeys = frozenset()
+ _defaultProxyOverrideKeys = frozenset()
_defaultGlobalKeys = frozenset((
PropertyName.fromElement(davxml.ACL),
PropertyName.fromElement(davxml.ResourceID),
@@ -117,7 +123,7 @@
PropertyName.fromElement(TwistedQuotaRootProperty),
))
- def __init__(self, defaultUser, shareeUser=None):
+ def __init__(self, defaultUser, shareeUser=None, proxyUser=None):
"""
Instantiate the property store for a user. The default is the default user
(owner) property to read in the case of global or shadowable properties.
@@ -128,12 +134,17 @@
@param shareeUser: the per user uid or None if the same as defaultUser
@type shareeUser: C{str}
+
+ @param proxyUser: the proxy uid or None if no proxy
+ @type proxyUser: C{str}
"""
assert(defaultUser is not None or shareeUser is not None)
self._defaultUser = shareeUser if defaultUser is None else defaultUser
self._perUser = defaultUser if shareeUser is None else shareeUser
+ self._proxyUser = self._perUser if proxyUser is None else proxyUser
self._shadowableKeys = set(AbstractPropertyStore._defaultShadowableKeys)
+ self._proxyOverrideKeys = set(AbstractPropertyStore._defaultProxyOverrideKeys)
self._globalKeys = set(AbstractPropertyStore._defaultGlobalKeys)
@@ -149,8 +160,13 @@
self._perUser = uid
- def setSpecialProperties(self, shadowableKeys, globalKeys):
+ def _setProxyUID(self, uid):
+ self._proxyUser = uid
+
+
+ def setSpecialProperties(self, shadowableKeys, globalKeys, proxyOverrideKeys):
self._shadowableKeys.update(shadowableKeys)
+ self._proxyOverrideKeys.update(proxyOverrideKeys)
self._globalKeys.update(globalKeys)
@@ -191,6 +207,13 @@
#
def __getitem__(self, key):
+ # 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
+
# Handle per-user behavior
if self.isShadowableProperty(key):
try:
@@ -208,11 +231,23 @@
# Handle per-user behavior
if self.isGlobalProperty(key):
return self._setitem_uid(key, value, self._defaultUser)
+ # Handle proxy behavior
+ elif self._proxyUser != self._perUser and self.isProxyOverrideProperty(key):
+ return self._setitem_uid(key, value, self._proxyUser)
+ # Remainder is per user
else:
return self._setitem_uid(key, value, self._perUser)
def __delitem__(self, key):
+ # 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
+
# Handle per-user behavior
if self.isShadowableProperty(key):
try:
@@ -245,11 +280,14 @@
self[key] = other[key]
- # Per-user property handling
def isShadowableProperty(self, key):
return key in self._shadowableKeys
+ def isProxyOverrideProperty(self, key):
+ return key in self._proxyOverrideKeys
+
+
def isGlobalProperty(self, key):
return key in self._globalKeys
Modified: CalendarServer/trunk/txdav/base/propertystore/sql.py
===================================================================
--- 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)
@@ -117,17 +117,19 @@
yield _cache_user_props(self._defaultUser)
if self._perUser != self._defaultUser:
yield _cache_user_props(self._perUser)
+ if self._proxyUser != self._perUser:
+ yield _cache_user_props(self._proxyUser)
@classmethod
@inlineCallbacks
- def load(cls, defaultuser, shareUser, txn, resourceID, created=False, notifyCallback=None):
+ def load(cls, defaultuser, shareUser, proxyUser, txn, resourceID, created=False, notifyCallback=None):
"""
@param notifyCallback: a callable used to trigger notifications when the
property store changes.
"""
self = cls.__new__(cls)
- super(PropertyStore, self).__init__(defaultuser, shareUser)
+ super(PropertyStore, self).__init__(defaultuser, shareUser, proxyUser)
self._txn = txn
self._resourceID = resourceID
if not self._txn.store().queryCachingEnabled():
@@ -141,7 +143,7 @@
@classmethod
@inlineCallbacks
- def forMultipleResources(cls, defaultUser, txn,
+ def forMultipleResources(cls, defaultUser, shareeUser, proxyUser, txn,
childColumn, parentColumn, parentID):
"""
Load all property stores for all objects in a collection. This is used
@@ -179,13 +181,13 @@
Where=parentColumn == parentID
)
rows = yield query.on(txn)
- stores = cls._createMultipleStores(defaultUser, txn, rows)
+ stores = cls._createMultipleStores(defaultUser, shareeUser, proxyUser, txn, rows)
returnValue(stores)
@classmethod
@inlineCallbacks
- def forMultipleResourcesWithResourceIDs(cls, defaultUser, txn, resourceIDs):
+ def forMultipleResourcesWithResourceIDs(cls, defaultUser, shareeUser, proxyUser, txn, resourceIDs):
"""
Load all property stores for all specified resources. This is used
to optimize Depth:1 operations on that collection, by loading all
@@ -214,13 +216,13 @@
Where=prop.RESOURCE_ID.In(Parameter("resourceIDs", len(resourceIDs)))
)
rows = yield query.on(txn, resourceIDs=resourceIDs)
- stores = cls._createMultipleStores(defaultUser, txn, rows)
+ stores = cls._createMultipleStores(defaultUser, shareeUser, proxyUser, txn, rows)
# Make sure we have a store for each resourceID even if no properties exist
for resourceID in resourceIDs:
if resourceID not in stores:
store = cls.__new__(cls)
- super(PropertyStore, store).__init__(defaultUser)
+ super(PropertyStore, store).__init__(defaultUser, shareeUser, proxyUser)
store._txn = txn
store._resourceID = resourceID
store._cached = {}
@@ -230,7 +232,7 @@
@classmethod
- def _createMultipleStores(cls, defaultUser, txn, rows):
+ def _createMultipleStores(cls, defaultUser, shareeUser, proxyUser, txn, rows):
"""
Create a set of stores for the set of rows passed in.
"""
@@ -245,7 +247,7 @@
if resource_id:
if resource_id not in createdStores:
store = cls.__new__(cls)
- super(PropertyStore, store).__init__(defaultUser)
+ super(PropertyStore, store).__init__(defaultUser, shareeUser, proxyUser)
store._txn = txn
store._resourceID = resource_id
store._cached = {}
@@ -253,7 +255,7 @@
createdStores[resource_id]._cached[(name, view_uid)] = value
elif object_resource_id:
store = cls.__new__(cls)
- super(PropertyStore, store).__init__(defaultUser)
+ super(PropertyStore, store).__init__(defaultUser, shareeUser, proxyUser)
store._txn = txn
store._resourceID = object_resource_id
store._cached = {}
Modified: CalendarServer/trunk/txdav/base/propertystore/test/base.py
===================================================================
--- 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)
@@ -140,29 +140,45 @@
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.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), 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), 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
@@ -170,8 +186,10 @@
name = propertyName("shadow")
- self.propertyStore1.setSpecialProperties((name,), ())
- self.propertyStore2.setSpecialProperties((name,), ())
+ self.propertyStore1.setSpecialProperties((name,), (), ())
+ self.propertyStore2.setSpecialProperties((name,), (), ())
+ self.propertyStore3.setSpecialProperties((name,), (), ())
+ self.propertyStore4.setSpecialProperties((name,), (), ())
value1 = propertyValue("Hello, World1!")
value2 = propertyValue("Hello, World2!")
@@ -180,29 +198,45 @@
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.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), 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)
@inlineCallbacks
@@ -213,8 +247,10 @@
name = propertyName("shadow")
- self.propertyStore1.setSpecialProperties((name,), ())
- self.propertyStore2.setSpecialProperties((name,), ())
+ self.propertyStore1.setSpecialProperties((name,), (), ())
+ self.propertyStore2.setSpecialProperties((name,), (), ())
+ self.propertyStore3.setSpecialProperties((name,), (), ())
+ self.propertyStore4.setSpecialProperties((name,), (), ())
value1 = propertyValue("Hello, World1!")
@@ -222,22 +258,34 @@
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)
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)
@inlineCallbacks
@@ -245,8 +293,10 @@
name = propertyName("global")
- self.propertyStore1.setSpecialProperties((), (name,))
- self.propertyStore2.setSpecialProperties((), (name,))
+ self.propertyStore1.setSpecialProperties((), (name,), ())
+ self.propertyStore2.setSpecialProperties((), (name,), ())
+ self.propertyStore3.setSpecialProperties((), (name,), ())
+ self.propertyStore4.setSpecialProperties((), (name,), ())
value1 = propertyValue("Hello, World1!")
value2 = propertyValue("Hello, World2!")
@@ -255,24 +305,250 @@
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.propertyStore2[name] = value2
yield self._changed(self.propertyStore2)
self.assertEquals(self.propertyStore1.get(name, None), value2)
self.assertEquals(self.propertyStore2.get(name, None), value2)
+ self.assertEquals(self.propertyStore3.get(name, None), value2)
+ 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), 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_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)
+
+
def test_iteration(self):
value = propertyValue("Hello, World!")
@@ -333,8 +609,8 @@
name = propertyName("shadow")
- self.propertyStore1.setSpecialProperties((name,), ())
- self.propertyStore2.setSpecialProperties((name,), ())
+ self.propertyStore1.setSpecialProperties((name,), (), ())
+ self.propertyStore2.setSpecialProperties((name,), (), ())
value1 = propertyValue("Hello, World1!")
Modified: CalendarServer/trunk/txdav/base/propertystore/test/test_sql.py
===================================================================
--- 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)
@@ -49,10 +49,11 @@
self.store = yield buildStore(self, self.notifierFactory)
self.addCleanup(self.maybeCommitLast)
self._txn = self.store.newTransaction()
- self.propertyStore = self.propertyStore1 = yield PropertyStore.load(
- "user01", None, self._txn, 1
- )
- self.propertyStore2 = yield PropertyStore.load("user01", "user02", self._txn, 1)
+ 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)
@inlineCallbacks
@@ -62,7 +63,11 @@
delattr(self, "_txn")
else:
result = None
- self.propertyStore = self.propertyStore1 = self.propertyStore2 = None
+ self.propertyStore = \
+ self.propertyStore1 = \
+ self.propertyStore2 = \
+ self.propertyStore3 = \
+ self.propertyStore4 = None
returnValue(result)
@@ -74,18 +79,31 @@
self._txn = self.store.newTransaction()
store = self.propertyStore1
- self.propertyStore = self.propertyStore1 = yield PropertyStore.load(
- "user01", None, self._txn, 1
- )
+ self.propertyStore = \
+ self.propertyStore1 = yield PropertyStore.load("user01", None, None, self._txn, 1)
self.propertyStore1._shadowableKeys = store._shadowableKeys
+ self.propertyStore1._proxyOverrideKeys = store._proxyOverrideKeys
self.propertyStore1._globalKeys = store._globalKeys
store = self.propertyStore2
- self.propertyStore2 = yield PropertyStore.load("user01", "user02", self._txn, 1)
+ self.propertyStore2 = yield PropertyStore.load("user01", "user02", None, self._txn, 1)
self.propertyStore2._shadowableKeys = store._shadowableKeys
+ self.propertyStore2._proxyOverrideKeys = store._proxyOverrideKeys
self.propertyStore2._globalKeys = store._globalKeys
+ 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
+ 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
+
+
@inlineCallbacks
def _abort(self, store):
if hasattr(self, "_txn"):
@@ -95,18 +113,31 @@
self._txn = self.store.newTransaction()
store = self.propertyStore1
- self.propertyStore = self.propertyStore1 = yield PropertyStore.load(
- "user01", None, self._txn, 1
- )
+ self.propertyStore = \
+ self.propertyStore1 = yield PropertyStore.load("user01", None, None, self._txn, 1)
self.propertyStore1._shadowableKeys = store._shadowableKeys
+ self.propertyStore1._proxyOverrideKeys = store._proxyOverrideKeys
self.propertyStore1._globalKeys = store._globalKeys
store = self.propertyStore2
- self.propertyStore2 = yield PropertyStore.load("user01", "user02", self._txn, 1)
+ self.propertyStore2 = yield PropertyStore.load("user01", "user02", None, self._txn, 1)
self.propertyStore2._shadowableKeys = store._shadowableKeys
+ self.propertyStore2._proxyOverrideKeys = store._proxyOverrideKeys
self.propertyStore2._globalKeys = store._globalKeys
+ 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
+ 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
+
+
@inlineCallbacks
def test_concurrentInsertion(self):
"""
@@ -126,7 +157,7 @@
pass
self.addCleanup(maybeAbortIt)
concurrentPropertyStore = yield PropertyStore.load(
- "user01", None, concurrentTxn, 1
+ "user01", None, None, concurrentTxn, 1
)
concurrentPropertyStore[pname] = pval1
race = []
@@ -155,8 +186,8 @@
def test_copy(self):
# Existing store
- store1_user1 = yield PropertyStore.load("user01", None, self._txn, 2)
- store1_user2 = yield PropertyStore.load("user01", "user02", self._txn, 2)
+ store1_user1 = yield PropertyStore.load("user01", None, None, self._txn, 2)
+ store1_user2 = yield PropertyStore.load("user01", "user02", None, self._txn, 2)
# Populate current store with data
props_user1 = (
@@ -178,18 +209,18 @@
self._txn = self.store.newTransaction()
# Existing store
- store1_user1 = yield PropertyStore.load("user01", None, self._txn, 2)
+ store1_user1 = yield PropertyStore.load("user01", None, None, self._txn, 2)
# New store
- store2_user1 = yield PropertyStore.load("user01", None, self._txn, 3)
+ store2_user1 = yield PropertyStore.load("user01", None, None, self._txn, 3)
# Do copy and check results
yield store2_user1.copyAllProperties(store1_user1)
self.assertEqual(store1_user1.keys(), store2_user1.keys())
- store1_user2 = yield PropertyStore.load("user01", "user02", self._txn, 2)
- store2_user2 = yield PropertyStore.load("user01", "user02", self._txn, 3)
+ store1_user2 = yield PropertyStore.load("user01", "user02", None, self._txn, 2)
+ store2_user2 = yield PropertyStore.load("user01", "user02", None, self._txn, 3)
self.assertEqual(store1_user2.keys(), store2_user2.keys())
@@ -197,7 +228,7 @@
def test_insert_delete(self):
# Existing store
- store1_user1 = yield PropertyStore.load("user01", None, self._txn, 2)
+ store1_user1 = yield PropertyStore.load("user01", None, None, self._txn, 2)
pname = propertyName("dummy1")
pvalue = propertyValue("value1-user1")
@@ -221,7 +252,7 @@
# Existing store - add a normal property
self.assertFalse("SQL.props:10/user01" in PropertyStore._cacher._memcacheProtocol._cache)
- store1_user1 = yield PropertyStore.load("user01", None, self._txn, 10)
+ store1_user1 = yield PropertyStore.load("user01", None, None, self._txn, 10)
self.assertTrue("SQL.props:10/user01" in PropertyStore._cacher._memcacheProtocol._cache)
pname1 = propertyName("dummy1")
@@ -237,7 +268,7 @@
# Existing store - add a large property
self._txn = self.store.newTransaction()
self.assertFalse("SQL.props:10/user01" in PropertyStore._cacher._memcacheProtocol._cache)
- store1_user1 = yield PropertyStore.load("user01", None, self._txn, 10)
+ store1_user1 = yield PropertyStore.load("user01", None, None, self._txn, 10)
self.assertTrue("SQL.props:10/user01" in PropertyStore._cacher._memcacheProtocol._cache)
pname2 = propertyName("dummy2")
@@ -253,7 +284,7 @@
# Try again - the cacher will fail large values
self._txn = self.store.newTransaction()
self.assertFalse("SQL.props:10/user01" in PropertyStore._cacher._memcacheProtocol._cache)
- store1_user1 = yield PropertyStore.load("user01", None, self._txn, 10)
+ store1_user1 = yield PropertyStore.load("user01", None, None, self._txn, 10)
self.assertFalse("SQL.props:10/user01" in store1_user1._cacher._memcacheProtocol._cache)
self.assertEqual(store1_user1[pname1], pvalue1)
@@ -280,7 +311,7 @@
# Existing store - add a normal property
self.assertFalse("SQL.props:10/user01" in PropertyStore._cacher._memcacheProtocol._cache)
- store1_user1 = yield PropertyStore.load("user01", None, self._txn, 10)
+ store1_user1 = yield PropertyStore.load("user01", None, None, self._txn, 10)
self.assertFalse("SQL.props:10/user01" in PropertyStore._cacher._memcacheProtocol._cache)
pname1 = propertyName("dummy1")
@@ -297,7 +328,7 @@
# Existing store - check a normal property
self.assertFalse("SQL.props:10/user01" in PropertyStore._cacher._memcacheProtocol._cache)
- store1_user1 = yield PropertyStore.load("user01", None, self._txn, 10)
+ store1_user1 = yield PropertyStore.load("user01", None, None, self._txn, 10)
self.assertFalse("SQL.props:10/user01" in PropertyStore._cacher._memcacheProtocol._cache)
self.assertEqual(store1_user1[pname1], pvalue1)
Modified: CalendarServer/trunk/txdav/base/propertystore/test/test_xattr.py
===================================================================
--- 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)
@@ -43,6 +43,12 @@
self.propertyStore1 = self.propertyStore
self.propertyStore2 = PropertyStore("user01", lambda : tempFile)
self.propertyStore2._setPerUserUID("user02")
+ 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")
def test_init(self):
Modified: CalendarServer/trunk/txdav/caldav/datastore/file.py
===================================================================
--- 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)
@@ -319,6 +319,7 @@
PropertyName.fromElement(customxml.GETCTag),
PropertyName.fromElement(caldavxml.SupportedCalendarComponentSet),
),
+ (),
)
@@ -748,6 +749,7 @@
PropertyName.fromElement(customxml.TwistedCalendarHasPrivateCommentsProperty),
PropertyName.fromElement(customxml.ScheduleChanges),
),
+ (),
)
Modified: CalendarServer/trunk/txdav/caldav/datastore/sql.py
===================================================================
--- 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)
@@ -48,7 +48,7 @@
from twisted.python import hashlib
from twisted.python.failure import Failure
-from twistedcaldav import caldavxml, customxml, ical
+from twistedcaldav import customxml, ical
from twistedcaldav.config import config
from twistedcaldav.datafilters.peruserdata import PerUserDataFilter
from twistedcaldav.dateops import normalizeForIndex, datetimeMktime, \
@@ -96,6 +96,7 @@
InvalidResourceMove, InvalidComponentForStoreError, \
NoSuchObjectResourceError, ConcurrentModification
from txdav.xml import element
+from txdav.xml.parser import WebDAVDocument
from txdav.idav import ChangeCategory
@@ -966,6 +967,10 @@
_supportedComponents = None
+ _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])
+
def __init__(self, *args, **kw):
"""
Initialize a calendar pointing at a record in a database.
@@ -1294,17 +1299,9 @@
def initPropertyStore(self, props):
# Setup peruser special properties
props.setSpecialProperties(
- # Shadowable
- (
- PropertyName.fromElement(caldavxml.CalendarDescription),
- PropertyName.fromElement(caldavxml.CalendarTimeZone),
- ),
-
- # Global
- (
- PropertyName.fromElement(customxml.GETCTag),
- PropertyName.fromElement(caldavxml.SupportedCalendarComponentSet),
- ),
+ self._shadowProperties,
+ self._globalProperties,
+ self._proxyProperties,
)
@@ -1353,9 +1350,9 @@
Sub-classes should override to expose the properties they care about.
"""
props = {}
- 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)])
+ for ename in (PropertyName.fromElement(element.DisplayName),) + self._shadowProperties:
+ if ename in self.properties():
+ props[ename.toString()] = self.properties()[ename].toxml()
return props
@@ -1366,15 +1363,15 @@
care about.
"""
# Initialize these for all shares
- 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()])
+ 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
# Only initialize these for direct shares
if self.direct():
- 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()])
+ 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
# FIXME: this is DAV-ish. Data store calendar objects don't have
@@ -2169,17 +2166,14 @@
cutype == "RESOURCE" and config.Scheduling.Options.TrackUnscheduledResourceData):
# Find current principal and update modified by details
- 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")
+ 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)
self._componentChanged = True
@@ -2197,7 +2191,7 @@
# Only DAV:owner is able to set the property to other than PUBLIC
if internal_state == ComponentUpdateState.NORMAL:
- if (self._txn._authz_uid is None or self.calendar().viewerHome().uid() != self._txn._authz_uid) and access != Component.ACCESS_PUBLIC:
+ if (self.calendar().viewerHome().uid() != self.calendar().viewerHome().authzuid()) and access != Component.ACCESS_PUBLIC:
raise InvalidCalendarAccessError("Private event access level change not allowed")
self.accessMode = access
@@ -3916,13 +3910,16 @@
def initPropertyStore(self, props):
- # Setup peruser special properties
+ # 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.
props.setSpecialProperties(
(
),
(
PropertyName.fromElement(customxml.ScheduleChanges),
),
+ (),
)
Modified: CalendarServer/trunk/txdav/caldav/datastore/test/test_implicit.py
===================================================================
--- 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)
@@ -306,18 +306,18 @@
"""
self.patch(config, "EnablePrivateEvents", True)
+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user02"
calendar_collection = (yield self.calendarUnderTest(home="user01"))
calendar = Component.fromString(data1)
- txn = self.transactionUnderTest()
- txn._authz_uid = "user02"
yield self.failUnlessFailure(calendar_collection.createCalendarObjectWithName("test.ics", calendar), InvalidCalendarAccessError)
yield self.commit()
# This one should be OK
+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user01"
calendar_collection = (yield self.calendarUnderTest(home="user01"))
calendar = Component.fromString(data1)
- txn = self.transactionUnderTest()
- txn._authz_uid = "user01"
yield calendar_collection.createCalendarObjectWithName("test.ics", calendar)
yield self.commit()
@@ -335,10 +335,10 @@
END:VCALENDAR
"""
+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user01"
calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
calendar = Component.fromString(data2)
- txn = self.transactionUnderTest()
- txn._authz_uid = "user01"
yield calendar_resource.setComponent(calendar)
yield self.commit()
@@ -516,10 +516,10 @@
END:VCALENDAR
"""
+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user01"
calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
calendar = Component.fromString(data2)
- txn = self.transactionUnderTest()
- txn._authz_uid = "user01"
yield calendar_resource.setComponent(calendar)
yield self.commit()
@@ -569,10 +569,10 @@
END:VCALENDAR
"""
+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user01"
calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
calendar = Component.fromString(data2)
- txn = self.transactionUnderTest()
- txn._authz_uid = "user01"
yield calendar_resource.setComponent(calendar)
yield self.commit()
@@ -623,10 +623,10 @@
END:VCALENDAR
"""
+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user01"
calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
calendar = Component.fromString(data2)
- txn = self.transactionUnderTest()
- txn._authz_uid = "user01"
yield calendar_resource.setComponent(calendar)
yield self.commit()
@@ -682,10 +682,10 @@
END:VCALENDAR
"""
+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user01"
calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
calendar = Component.fromString(data2)
- txn = self.transactionUnderTest()
- txn._authz_uid = "user01"
yield calendar_resource.setComponent(calendar)
yield self.commit()
@@ -789,10 +789,10 @@
END:VCALENDAR
"""
+ txn = self.transactionUnderTest()
+ txn._authz_uid = "user01"
calendar_resource = (yield self.calendarObjectUnderTest(name="test.ics", home="user01",))
calendar = Component.fromString(data2)
- txn = self.transactionUnderTest()
- txn._authz_uid = "user01"
result = (yield calendar_resource.setComponent(calendar))
yield self.commit()
self.assertTrue(result)
Modified: CalendarServer/trunk/txdav/caldav/datastore/test/test_sql_sharing.py
===================================================================
--- 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)
@@ -23,6 +23,10 @@
populateCalendarsFrom
from txdav.common.datastore.sql_tables import _BIND_MODE_READ, \
_BIND_STATUS_INVITED, _BIND_MODE_DIRECT, _BIND_STATUS_ACCEPTED
+from txdav.base.propertystore.base import PropertyName
+from txdav.xml.base import WebDAVTextElement
+from twistedcaldav import customxml
+from txdav.xml.element import registerElement, registerElementClass
class BaseSharingTests(CommonCommonTests, TestCase):
@@ -489,7 +493,94 @@
yield self.commit()
+ @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"
+ 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()
+
+
+
class SharingRevisions(BaseSharingTests):
"""
Test store-based sharing and interaction with revision table.
Modified: CalendarServer/trunk/txdav/carddav/datastore/file.py
===================================================================
--- 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)
@@ -139,6 +139,7 @@
(
PropertyName.fromElement(customxml.GETCTag),
),
+ (),
)
Modified: CalendarServer/trunk/txdav/carddav/datastore/sql.py
===================================================================
--- 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)
@@ -39,7 +39,6 @@
from twisted.internet.defer import inlineCallbacks, returnValue
from twisted.python import hashlib
-from twistedcaldav import carddavxml, customxml
from twistedcaldav.config import config
from twistedcaldav.memcacher import Memcacher
from twistedcaldav.vcard import Component as VCard, InvalidVCardDataError, Property, \
@@ -65,6 +64,7 @@
AllRetriesFailed, ObjectResourceNameAlreadyExistsError, \
SyncTokenValidException, IndexedSearchException
from txdav.xml import element
+from txdav.xml.parser import WebDAVDocument
from zope.interface.declarations import implements
@@ -87,9 +87,9 @@
_cacher = Memcacher("SQL.adbkhome", pickle=True, key_normalization=False)
- def __init__(self, transaction, ownerUID):
+ def __init__(self, transaction, ownerUID, authzUID=None):
- super(AddressBookHome, self).__init__(transaction, ownerUID)
+ super(AddressBookHome, self).__init__(transaction, ownerUID, authzUID=authzUID)
self._addressbookPropertyStoreID = None
self._addressbook = None
@@ -467,6 +467,9 @@
"UID": _objectSchema.UID,
}
+ _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])
@classmethod
@inlineCallbacks
@@ -797,6 +800,7 @@
props = yield PropertyStore.load(
self.ownerHome().uid(),
self.viewerHome().uid(),
+ None,
self._txn,
self.ownerHome()._addressbookPropertyStoreID, # not ._resourceID as in CommonHomeChild._loadPropertyStore()
notifyCallback=self.notifyPropertyChanged
@@ -807,25 +811,22 @@
def initPropertyStore(self, props):
# Setup peruser special properties
props.setSpecialProperties(
- (
- PropertyName.fromElement(carddavxml.AddressBookDescription),
- ),
- (
- PropertyName.fromElement(customxml.GETCTag),
- ),
+ self._shadowProperties,
+ self._globalProperties,
+ self._proxyProperties,
)
def getInviteCopyProperties(self):
"""
- Get a dictionary of property name/values (as strings) for properties that are shadowable and
+ Get a dictionary of property name/values (as XML strings) for properties that are shadowable and
need to be copied to a sharee's collection when an external (cross-pod) share is created.
Sub-classes should override to expose the properties they care about.
"""
props = {}
- for elem in (element.DisplayName, carddavxml.AddressBookDescription,):
- if PropertyName.fromElement(elem) in self.properties():
- props[elem.sname()] = str(self.properties()[PropertyName.fromElement(elem)])
+ for ename in (PropertyName.fromElement(element.DisplayName),) + self._shadowProperties:
+ if ename in self.properties():
+ props[ename.toString()] = self.properties()[ename].toxml()
return props
@@ -836,15 +837,15 @@
care about.
"""
# Initialize these for all shares
- 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()])
+ 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
# Only initialize these for direct shares
if self.direct():
- 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()])
+ 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
def contentType(self):
@@ -1167,7 +1168,7 @@
# Get property stores for all these child resources (if any found)
addressbookPropertyStoreIDs = [ownerHomeItem._addressbookPropertyStoreID for ownerHomeItem in ownerHomeToDataRowMap]
propertyStores = yield PropertyStore.forMultipleResourcesWithResourceIDs(
- home.uid(), home._txn, addressbookPropertyStoreIDs
+ home.uid(), None, None, home._txn, addressbookPropertyStoreIDs
)
addressbookResourceIDs = [ownerHomeItem.addressbook()._resourceID for ownerHomeItem in ownerHomeToDataRowMap]
Modified: CalendarServer/trunk/txdav/common/datastore/file.py
===================================================================
--- 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)
@@ -1701,6 +1701,7 @@
(
PropertyName.fromElement(customxml.NotificationType),
),
+ (),
)
Modified: CalendarServer/trunk/txdav/common/datastore/sql.py
===================================================================
--- 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)
@@ -694,7 +694,7 @@
).on(self)
- def _determineMemo(self, storeType, uid, create=False):
+ def _determineMemo(self, storeType, uid, create=False, authzUID=None):
"""
Determine the memo dictionary to use for homeWithUID.
"""
@@ -720,19 +720,27 @@
@memoizedKey("uid", _determineMemo)
- def homeWithUID(self, storeType, uid, create=False):
+ 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.
+ """
if storeType not in (ECALENDARTYPE, EADDRESSBOOKTYPE):
raise RuntimeError("Unknown home type.")
- return self._homeClass[storeType].homeWithUID(self, uid, create)
+ return self._homeClass[storeType].homeWithUID(self, uid, create, authzUID)
- def calendarHomeWithUID(self, uid, create=False):
- return self.homeWithUID(ECALENDARTYPE, uid, create=create)
+ def calendarHomeWithUID(self, uid, create=False, authzUID=None):
+ return self.homeWithUID(ECALENDARTYPE, uid, create=create, authzUID=authzUID)
- def addressbookHomeWithUID(self, uid, create=False):
- return self.homeWithUID(EADDRESSBOOKTYPE, uid, create=create)
+ def addressbookHomeWithUID(self, uid, create=False, authzUID=None):
+ return self.homeWithUID(EADDRESSBOOKTYPE, uid, create=create, authzUID=authzUID)
@inlineCallbacks
@@ -740,10 +748,10 @@
"""
Load a calendar or addressbook home by its integer resource ID.
"""
- uid = (yield self._homeClass[storeType]
- .homeUIDWithResourceID(self, rid))
+ uid = (yield self._homeClass[storeType].homeUIDWithResourceID(self, rid))
if uid:
- result = (yield self.homeWithUID(storeType, uid))
+ # Always get the owner's view of the home = i.e., authzUID=uid
+ result = (yield self.homeWithUID(storeType, uid, authzUID=uid))
else:
result = None
returnValue(result)
@@ -2838,7 +2846,7 @@
@classmethod
@inlineCallbacks
- def makeClass(cls, transaction, ownerUID, no_cache=False):
+ def makeClass(cls, transaction, ownerUID, no_cache=False, authzUID=None):
"""
Build the actual home class taking into account the possibility that we might need to
switch in the external version of the class.
@@ -2850,14 +2858,20 @@
@param no_cache: should cached query be used
@type no_cache: C{bool}
"""
- home = cls(transaction, ownerUID)
+ home = cls(transaction, ownerUID, authzUID=authzUID)
actualHome = yield home.initFromStore(no_cache)
returnValue(actualHome)
- def __init__(self, transaction, ownerUID):
+ def __init__(self, transaction, ownerUID, authzUID=None):
self._txn = transaction
self._ownerUID = ownerUID
+ 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
self._resourceID = None
self._status = _HOME_STATUS_NORMAL
self._dataVersion = None
@@ -3052,11 +3066,11 @@
@classmethod
@inlineCallbacks
- def homeWithUID(cls, txn, uid, create=False):
+ def homeWithUID(cls, txn, uid, create=False, authzUID=None):
"""
@param uid: I'm going to assume uid is utf-8 encoded bytes
"""
- homeObject = yield cls.makeClass(txn, uid)
+ homeObject = yield cls.makeClass(txn, uid, authzUID=authzUID)
if homeObject is not None:
returnValue(homeObject)
else:
@@ -3091,7 +3105,7 @@
yield savepoint.rollback(txn)
# Retry the query - row may exist now, if not re-raise
- homeObject = yield cls.makeClass(txn, uid)
+ homeObject = yield cls.makeClass(txn, uid, authzUID=authzUID)
if homeObject:
returnValue(homeObject)
else:
@@ -3102,7 +3116,7 @@
# Note that we must not cache the owner_uid->resource_id
# mapping in _cacher when creating as we don't want that to appear
# until AFTER the commit
- home = yield cls.makeClass(txn, uid, no_cache=True)
+ home = yield cls.makeClass(txn, uid, no_cache=True, authzUID=authzUID)
yield home.createdHome()
returnValue(home)
@@ -3140,6 +3154,15 @@
return self._ownerUID
+ def authzuid(self):
+ """
+ Retrieve the unique identifier of the user accessing the data in this home.
+
+ @return: a string.
+ """
+ return self._authzUID
+
+
def external(self):
"""
Is this an external home.
@@ -3540,9 +3563,13 @@
@inlineCallbacks
def _loadPropertyStore(self):
+
+ # Use any authz uid in place of the viewer uid so delegates have their own
+ # set of properties
props = yield PropertyStore.load(
self.uid(),
self.uid(),
+ self.authzuid(),
self._txn,
self._resourceID,
notifyCallback=self.notifyChanged
@@ -4652,6 +4679,9 @@
def ownerView(self):
"""
Return the owner resource counterpart of this shared resource.
+
+ Note we have to play a trick with the property store to coerce it to match
+ the per-user properties for the owner.
"""
# Get the child of the owner home that has the same resource id as the owned one
ownerView = yield self.ownerHome().childWithID(self.id())
@@ -4662,10 +4692,13 @@
def shareeView(self, shareeUID):
"""
Return the shared resource counterpart of this owned resource for the specified sharee.
+
+ Note we have to play a trick with the property store to coerce it to match
+ the per-user properties for the sharee.
"""
# Get the child of the sharee home that has the same resource id as the owned one
- shareeHome = yield self._txn.homeWithUID(self._home._homeType, shareeUID)
+ shareeHome = yield self._txn.homeWithUID(self._home._homeType, shareeUID, authzUID=shareeUID)
shareeView = (yield shareeHome.allChildWithID(self.id())) if shareeHome is not None else None
returnValue(shareeView)
@@ -5434,7 +5467,7 @@
childResourceIDs = [dataRow[2] for dataRow in dataRows]
propertyStores = yield PropertyStore.forMultipleResourcesWithResourceIDs(
- home.uid(), home._txn, childResourceIDs
+ home.uid(), None, None, home._txn, childResourceIDs
)
# Get revisions
@@ -6216,9 +6249,12 @@
@inlineCallbacks
def _loadPropertyStore(self, props=None):
if props is None:
+ # Use any authz uid in place of the viewer uid so delegates have their own
+ # set of properties
props = yield PropertyStore.load(
self.ownerHome().uid(),
self.viewerHome().uid(),
+ self.viewerHome().authzuid(),
self._txn,
self._resourceID,
notifyCallback=self.notifyPropertyChanged
@@ -6534,6 +6570,8 @@
if parent.objectResourcesHaveProperties():
propertyStores = (yield PropertyStore.forMultipleResources(
parent._home.uid(),
+ None,
+ None,
parent._txn,
cls._objectSchema.RESOURCE_ID,
cls._objectSchema.PARENT_RESOURCE_ID,
@@ -6611,6 +6649,8 @@
if parent.objectResourcesHaveProperties():
propertyStores = (yield PropertyStore.forMultipleResourcesWithResourceIDs(
parent._home.uid(),
+ None,
+ None,
parent._txn,
tuple([row[0] for row in dataRows]),
))
@@ -6789,8 +6829,9 @@
if props is None:
if self._parentCollection.objectResourcesHaveProperties():
props = yield PropertyStore.load(
+ self._parentCollection.ownerHome().uid(),
self._parentCollection.viewerHome().uid(),
- self._parentCollection.ownerHome().uid(),
+ self._parentCollection.viewerHome().authzuid(),
self._txn,
self._resourceID,
created=created
@@ -7153,6 +7194,7 @@
self._propertyStore = yield PropertyStore.load(
self._uid,
self._uid,
+ None,
self._txn,
self._resourceID,
notifyCallback=self.notifyChanged
@@ -7492,6 +7534,8 @@
# Get property stores for all these child resources (if any found)
propertyStores = (yield PropertyStore.forMultipleResources(
parent.uid(),
+ None,
+ None,
parent._txn,
schema.NOTIFICATION.RESOURCE_ID,
schema.NOTIFICATION.NOTIFICATION_HOME_RESOURCE_ID,
Modified: CalendarServer/trunk/txdav/common/datastore/sql_external.py
===================================================================
--- 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)
@@ -149,6 +149,7 @@
props = yield PropertyStore.load(
self.uid(),
self.uid(),
+ None,
self._txn,
self._resourceID,
notifyCallback=self.notifyChanged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20140609/184dc4e1/attachment-0001.html>
More information about the calendarserver-changes
mailing list