[CalendarServer-changes] [11037] CalendarServer/branches/users/cdaboo/store-scheduling
source_changes at macosforge.org
source_changes at macosforge.org
Fri Apr 12 13:32:36 PDT 2013
Revision: 11037
http://trac.calendarserver.org//changeset/11037
Author: cdaboo at apple.com
Date: 2013-04-12 13:32:36 -0700 (Fri, 12 Apr 2013)
Log Message:
-----------
COPY/MOVE uses new store api.
Modified Paths:
--------------
CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/copymove.py
CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/copymove_contact.py
CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/storebridge.py
CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/sql.py
CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/icalendarstore.py
CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/sql.py
Modified: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/copymove.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/copymove.py 2013-04-12 19:42:10 UTC (rev 11036)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/copymove.py 2013-04-12 20:32:36 UTC (rev 11037)
@@ -53,6 +53,8 @@
the destination if its a calendar collection.
"""
+ raise AssertionError("Never use this")
+
# Copy of calendar collections isn't allowed.
if isPseudoCalendarCollectionResource(self):
returnValue(responsecode.FORBIDDEN)
@@ -128,6 +130,8 @@
since its effectively being deleted. We do need to do an index update for
the destination if its a calendar collection
"""
+ raise AssertionError("Never use this")
+
result, sourcecal, sourceparent, destination_uri, destination, destinationcal, destinationparent = (yield checkForCalendarAction(self, request))
if not result:
is_calendar_collection = isPseudoCalendarCollectionResource(self)
Modified: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/copymove_contact.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/copymove_contact.py 2013-04-12 19:42:10 UTC (rev 11036)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/method/copymove_contact.py 2013-04-12 20:32:36 UTC (rev 11037)
@@ -47,6 +47,8 @@
is not being changed in any way. We do need to do an index update for
the destination if its an addressbook collection.
"""
+ raise AssertionError("Never use this")
+
# Copy of addressbook collections isn't allowed.
if isAddressBookCollectionResource(self):
returnValue(responsecode.FORBIDDEN)
@@ -120,6 +122,8 @@
since its effectively being deleted. We do need to do an index update for
the destination if its an addressbook collection
"""
+ raise AssertionError("Never use this")
+
result, sourceadbk, sourceparent, destination_uri, destination, destinationadbk, destinationparent = (yield checkForAddressBookAction(self, request))
if not result or not destinationadbk:
Modified: CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/storebridge.py 2013-04-12 19:42:10 UTC (rev 11036)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/twistedcaldav/storebridge.py 2013-04-12 20:32:36 UTC (rev 11037)
@@ -28,8 +28,7 @@
from twext.web2.http_headers import ETag, MimeType, MimeDisposition
from twext.web2.responsecode import \
FORBIDDEN, NO_CONTENT, NOT_FOUND, CREATED, CONFLICT, PRECONDITION_FAILED, \
- BAD_REQUEST, OK, INSUFFICIENT_STORAGE_SPACE, SERVICE_UNAVAILABLE, \
- INTERNAL_SERVER_ERROR
+ BAD_REQUEST, OK, INSUFFICIENT_STORAGE_SPACE, SERVICE_UNAVAILABLE
from twext.web2.stream import ProducerStream, readStream, MemoryStream
from twisted.internet.defer import succeed, inlineCallbacks, returnValue, maybeDeferred
@@ -64,7 +63,8 @@
AttachmentDropboxNotAllowed, InvalidComponentTypeError, \
TooManyAttendeesError, InvalidCalendarAccessError, ValidOrganizerError, \
UIDExistsError, InvalidUIDError, InvalidPerUserDataMerge, \
- AttendeeAllowedError, ResourceDeletedError, InvalidComponentForStoreError
+ AttendeeAllowedError, ResourceDeletedError, InvalidComponentForStoreError, \
+ InvalidResourceMove
from txdav.common.datastore.sql_tables import _BIND_MODE_READ, _BIND_MODE_WRITE, \
_BIND_MODE_DIRECT
from txdav.common.icommondatastore import NoSuchObjectResourceError, \
@@ -2197,6 +2197,11 @@
response.headers.setHeader("content-type", self.contentType())
returnValue(response)
+ # The following are used to map store exceptions into HTTP error responses
+ StoreExceptionsStatusErrors = set()
+ StoreExceptionsErrors = {}
+ StoreMoveExceptionsStatusErrors = set()
+ StoreMoveExceptionsErrors = {}
@requiresPermissions(fromParent=[davxml.Unbind()])
def http_DELETE(self, request):
@@ -2210,6 +2215,14 @@
return self.storeRemove(request)
+ def http_COPY(self, request):
+ """
+ Copying of calendar data isn't allowed.
+ """
+ # FIXME: no direct tests
+ return FORBIDDEN
+
+
@inlineCallbacks
def http_MOVE(self, request):
"""
@@ -2271,11 +2284,32 @@
# May need to add a location header
addLocation(request, destination_uri)
- storer = self.storeResource(request, parent, destination, destination_uri, destinationparent, True, None)
- result = (yield storer.move())
- returnValue(result)
+ try:
+ response = (yield self.storeMove(request, destinationparent, destination.name()))
+ returnValue(response)
+ # Handle the various store errors
+ except Exception as err:
+ # Grab the current exception state here so we can use it in a re-raise - we need this because
+ # an inlineCallback might be called and that raises an exception when it returns, wiping out the
+ # original exception "context".
+ ex = Failure()
+
+ if type(err) in self.StoreMoveExceptionsStatusErrors:
+ raise HTTPError(StatusResponse(responsecode.FORBIDDEN, str(err)))
+
+ elif type(err) in self.StoreMoveExceptionsErrors:
+ raise HTTPError(ErrorResponse(
+ responsecode.FORBIDDEN,
+ self.StoreMoveExceptionsErrors[type(err)],
+ str(err),
+ ))
+ else:
+ # Return the original failure (exception) state
+ ex.raiseException()
+
+
def http_PROPPATCH(self, request):
"""
No dead properties allowed on object resources.
@@ -2332,12 +2366,7 @@
@type destination_name: C{str}
"""
- try:
- yield self._newStoreObject.moveTo(destinationparent._newStoreObject, destination_name)
- except Exception, e:
- log.err(e)
- raise HTTPError(INTERNAL_SERVER_ERROR)
-
+ yield self._newStoreObject.moveTo(destinationparent._newStoreObject, destination_name)
returnValue(CREATED)
@@ -2536,6 +2565,17 @@
AttachmentStoreValidManagedID: (caldav_namespace, "valid-managed-id"),
}
+ StoreMoveExceptionsStatusErrors = set((
+ ObjectResourceNameNotAllowedError,
+ ObjectResourceNameAlreadyExistsError,
+ ))
+
+ StoreMoveExceptionsErrors = {
+ TooManyObjectResourcesError: customxml.MaxResources(),
+ InvalidResourceMove: (calendarserver_namespace, "valid-move"),
+ InvalidComponentTypeError: (caldav_namespace, "supported-component"),
+ }
+
@inlineCallbacks
def http_PUT(self, request):
@@ -2578,6 +2618,17 @@
"Can't parse calendar data"
))
+ # 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.guid
+
try:
response = (yield self.storeComponent(component))
except ResourceDeletedError:
@@ -2586,6 +2637,19 @@
response = responsecode.CREATED if self.exists() else responsecode.NO_CONTENT
response = IResponse(response)
+ if self._newStoreObject.isScheduleObject:
+ # Add a response header
+ response.headers.setHeader("Schedule-Tag", self._newStoreObject.scheduleTag)
+
+ # Must not set ETag in response if data changed
+ if self._newStoreObject._componentChanged:
+ def _removeEtag(request, response):
+ response.headers.removeHeader('etag')
+ return response
+ _removeEtag.handleErrors = True
+
+ request.addResponseFilter(_removeEtag, atEnd=True)
+
# Look for Prefer header
prefer = request.headers.getHeader("prefer", {})
returnRepresentation = any([key == "return" and value == "representation" for key, value, _ignore_args in prefer])
@@ -2618,8 +2682,8 @@
str(err),
))
elif isinstance(err, ValueError):
- log.err("Error while handling (calendar) PUT: %s" % (e,))
- raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, str(e)))
+ log.err("Error while handling (calendar) PUT: %s" % (err,))
+ raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, str(err)))
else:
# Return the original failure (exception) state
ex.raiseException()
Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/sql.py 2013-04-12 19:42:10 UTC (rev 11036)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/datastore/sql.py 2013-04-12 20:32:36 UTC (rev 11037)
@@ -46,7 +46,7 @@
from twext.web2.http_headers import MimeType, generateContentType
from twext.web2.stream import readStream
-from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.internet.defer import inlineCallbacks, returnValue, succeed
from twisted.python import hashlib
from twistedcaldav import caldavxml, customxml
@@ -72,7 +72,7 @@
InvalidUIDError, UIDExistsError, ResourceDeletedError, \
AttendeeAllowedError, InvalidPerUserDataMerge, ComponentUpdateState, \
ValidOrganizerError, ShareeAllowedError, ComponentRemoveState, \
- InvalidComponentForStoreError
+ InvalidComponentForStoreError, InvalidResourceMove
from txdav.caldav.icalendarstore import QuotaExceeded
from txdav.common.datastore.sql import CommonHome, CommonHomeChild, \
CommonObjectResource, ECALENDARTYPE
@@ -330,7 +330,7 @@
def fullName(self):
- return "%s %s" % (self.principal_uid[:4], self.principal_uid[4:])
+ return "%s %s" % (self.principal_uid[:4].capitalize(), self.principal_uid[4:])
def displayName(self):
@@ -2378,6 +2378,43 @@
returnValue(self._cachedCommponentPerUser[user_uuid])
+ def moveValidation(self, destination, name):
+ """
+ Validate whether a move to the specified collection is allowed.
+
+ @param destination: destination calendar collection
+ @type destination: L{CalendarCollection}
+ @param name: name of new resource
+ @type name: C{str}
+ """
+
+ # Calendar to calendar moves are OK if the resource (viewer) owner is the same.
+ # Use resourceOwnerPrincipal for this as that takes into account sharing such that the
+ # returned principal relates to the URI path used to access the resource rather than the
+ # underlying resource owner (sharee).
+ sourceowner = self.calendar().viewerHome().uid()
+ destowner = destination.viewerHome().uid()
+
+ if sourceowner != destowner:
+ msg = "Calendar-to-calendar moves with different homes are not supported."
+ log.debug(msg)
+ raise InvalidResourceMove(msg)
+
+ # Calendar to calendar moves where Organizer is present are not OK if the owners are different.
+ sourceowner = self.calendar().ownerHome().uid()
+ destowner = destination.ownerHome().uid()
+
+ if sourceowner != destowner and self._schedule_object:
+ msg = "Calendar-to-calendar moves with an organizer property present and different owners are not supported."
+ log.debug(msg)
+ raise InvalidResourceMove(msg)
+
+ # NB there is no need to do a UID lock and test here as we are moving an existing resource
+ # with the already imposed constraint of unique UIDs.
+
+ return succeed(None)
+
+
def remove(self, implicitly=True):
return self._removeInternal(
internal_state=ComponentRemoveState.NORMAL if implicitly else ComponentRemoveState.NORMAL_NO_IMPLICIT
Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/icalendarstore.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/icalendarstore.py 2013-04-12 19:42:10 UTC (rev 11036)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/caldav/icalendarstore.py 2013-04-12 20:32:36 UTC (rev 11037)
@@ -199,6 +199,13 @@
+class InvalidResourceMove(CommonStoreError):
+ """
+ Moving a resource failed.
+ """
+
+
+
class AttachmentStoreFailed(Exception):
"""
Unable to store an attachment.
Modified: CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/sql.py
===================================================================
--- CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/sql.py 2013-04-12 19:42:10 UTC (rev 11036)
+++ CalendarServer/branches/users/cdaboo/store-scheduling/txdav/common/datastore/sql.py 2013-04-12 20:32:36 UTC (rev 11037)
@@ -3770,15 +3770,26 @@
@type newname: C{str} or C{None} for existing name
"""
- if newname and newname.startswith("."):
- raise ObjectResourceNameNotAllowedError(newname)
-
name = child.name()
uid = child.uid()
if newname is None:
newname = name
+ # Create => a new resource name
+ if newname.startswith("."):
+ raise ObjectResourceNameNotAllowedError(newname)
+
+ # Make sure name is not already used - i.e., overwrite not allowed
+ if (yield newparent.objectResourceWithName(newname)) is not None:
+ raise ObjectResourceNameAlreadyExistsError(newname)
+
+ # Apply check to the size of the collection
+ if config.MaxResourcesPerCollection:
+ child_count = (yield self.countObjectResources())
+ if child_count >= config.MaxResourcesPerCollection:
+ raise TooManyObjectResourcesError()
+
# Clean this collections cache and signal sync change
self._objects.pop(name, None)
self._objects.pop(uid, None)
@@ -4325,6 +4336,7 @@
return Delete(cls._objectSchema, Where=cls._objectSchema.RESOURCE_ID == Parameter("resourceID"))
+ @inlineCallbacks
def moveTo(self, destination, name):
"""
Move object to another collection.
@@ -4335,11 +4347,14 @@
@type name: C{str} or C{None} to use existing name
"""
- if name and name.startswith("."):
- raise ObjectResourceNameNotAllowedError(name)
- return self._parentCollection.moveObjectResource(self, destination, name)
+ yield self.moveValidation(destination, name)
+ yield self._parentCollection.moveObjectResource(self, destination, name)
+ def moveValidation(self, destination, name):
+ raise NotImplementedError
+
+
@inlineCallbacks
def remove(self):
yield self._deleteQuery.on(self._txn, NoSuchObjectResourceError,
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130412/a6767bee/attachment-0001.html>
More information about the calendarserver-changes
mailing list