[CalendarServer-changes] [5239] CalendarServer/branches/users/cdaboo/shared-calendars-5187

source_changes at macosforge.org source_changes at macosforge.org
Thu Mar 4 08:46:54 PST 2010


Revision: 5239
          http://trac.macosforge.org/projects/calendarserver/changeset/5239
Author:   cdaboo at apple.com
Date:     2010-03-04 08:46:51 -0800 (Thu, 04 Mar 2010)
Log Message:
-----------
Fine grained config options for different aspects of sharing. Validate userids and return multistatus for failed
responses.

Modified Paths:
--------------
    CalendarServer/branches/users/cdaboo/shared-calendars-5187/conf/caldavd-apple.plist
    CalendarServer/branches/users/cdaboo/shared-calendars-5187/conf/caldavd-test.plist
    CalendarServer/branches/users/cdaboo/shared-calendars-5187/conf/caldavd.plist
    CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/customxml.py
    CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/directory/calendar.py
    CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/directory/principal.py
    CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/resource.py
    CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/sharing.py
    CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/static.py
    CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/stdconfig.py
    CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/test/test_sharing.py

Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/conf/caldavd-apple.plist
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/conf/caldavd-apple.plist	2010-03-04 01:50:42 UTC (rev 5238)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/conf/caldavd-apple.plist	2010-03-04 16:46:51 UTC (rev 5239)
@@ -477,11 +477,27 @@
     <key>EnablePrivateEvents</key>
     <true/>
 
-    <!-- Shared Calendars -->
-    <key>EnableSharing</key>
-    <true/>
+    <!-- Shared Calendars & Address Books -->
+    <key>Sharing</key>
+    <dict>
+    	<key>Enabled</key>
+    	<true/>
+    	<key>AllowExternalUsers</key>
+    	<false/>
+    	<key>Calendars</key>
+    	<dict>
+    		<key>Enabled</key>
+    		<true/>
+    		<key>AllowScheduling</key>
+    		<false/>
+    	</dict>
+    	<key>AddressBooks</key>
+    	<dict>
+    		<key>Enabled</key>
+    		<false/>
+    	</dict>
+    </dict>
 
-
     <!--
         Miscellaneous items
       -->

Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/conf/caldavd-test.plist
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/conf/caldavd-test.plist	2010-03-04 01:50:42 UTC (rev 5238)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/conf/caldavd-test.plist	2010-03-04 16:46:51 UTC (rev 5239)
@@ -669,11 +669,27 @@
     <key>EnableTimezoneService</key>
     <true/>
 
-    <!-- Shared Calendars -->
-    <key>EnableSharing</key>
-    <true/>
+    <!-- Shared Calendars & Address Books -->
+    <key>Sharing</key>
+    <dict>
+    	<key>Enabled</key>
+    	<true/>
+    	<key>AllowExternalUsers</key>
+    	<false/>
+    	<key>Calendars</key>
+    	<dict>
+    		<key>Enabled</key>
+    		<true/>
+    		<key>AllowScheduling</key>
+    		<false/>
+    	</dict>
+    	<key>AddressBooks</key>
+    	<dict>
+    		<key>Enabled</key>
+    		<false/>
+    	</dict>
+    </dict>
 
-
     <!--
         Miscellaneous items
       -->

Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/conf/caldavd.plist
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/conf/caldavd.plist	2010-03-04 01:50:42 UTC (rev 5238)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/conf/caldavd.plist	2010-03-04 16:46:51 UTC (rev 5239)
@@ -466,9 +466,26 @@
     <key>EnablePrivateEvents</key>
     <true/>
 
-    <!-- Shared Calendars -->
-    <key>EnableSharing</key>
-    <true/>
+    <!-- Shared Calendars & Address Books -->
+    <key>Sharing</key>
+    <dict>
+    	<key>Enabled</key>
+    	<true/>
+    	<key>AllowExternalUsers</key>
+    	<false/>
+    	<key>Calendars</key>
+    	<dict>
+    		<key>Enabled</key>
+    		<true/>
+    		<key>AllowScheduling</key>
+    		<false/>
+    	</dict>
+    	<key>AddressBooks</key>
+    	<dict>
+    		<key>Enabled</key>
+    		<false/>
+    	</dict>
+    </dict>
 
 
     <!--

Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/customxml.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/customxml.py	2010-03-04 01:50:42 UTC (rev 5238)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/customxml.py	2010-03-04 16:46:51 UTC (rev 5239)
@@ -681,6 +681,7 @@
         (calendarserver_namespace, "invite-deleted" )    : (0, 1),
         (calendarserver_namespace, "invite-accepted" )   : (0, 1),
         (calendarserver_namespace, "invite-declined" )   : (0, 1),
+        (calendarserver_namespace, "invite-invalid" )    : (0, 1),
     }
 
 class InviteAccess (davxml.WebDAVElement):
@@ -720,6 +721,10 @@
     namespace = calendarserver_namespace
     name = "invite-declined"
 
+class InviteStatusInvalid (davxml.WebDAVEmptyElement):
+    namespace = calendarserver_namespace
+    name = "invite-invalid"
+
 class HostURL (davxml.WebDAVElement):
     """
     The source for a shared calendar

Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/directory/calendar.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/directory/calendar.py	2010-03-04 01:50:42 UTC (rev 5238)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/directory/calendar.py	2010-03-04 16:46:51 UTC (rev 5239)
@@ -278,7 +278,7 @@
             childlist += (
                 ("freebusy", FreeBusyURLResource),
             )
-        if config.EnableSharing:
+        if config.Sharing.Enabled and config.Sharing.Calendars.Enabled:
             childlist += (
                 ("notification", NotificationCollectionResource),
             )

Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/directory/principal.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/directory/principal.py	2010-03-04 01:50:42 UTC (rev 5238)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/directory/principal.py	2010-03-04 16:46:51 UTC (rev 5239)
@@ -895,7 +895,7 @@
     def notificationCollection(self, request):
         
         notification = None
-        if config.EnableSharing:
+        if config.Sharing.Enabled:
             home = self.calendarHome()
             if home is not None:    
                 notification = home.getChild("notification")
@@ -919,7 +919,7 @@
             return None
 
     def notificationURL(self):
-        if config.EnableSharing:
+        if config.Sharing.Enabled:
             return self._homeChildURL("notification/")
         else:
             return None

Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/resource.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/resource.py	2010-03-04 01:50:42 UTC (rev 5238)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/resource.py	2010-03-04 16:46:51 UTC (rev 5239)
@@ -114,7 +114,7 @@
         extra_compliance += customxml.calendarserver_principal_property_search_compliance
         if config.EnableCardDAV:
             extra_compliance += carddavxml.carddav_compliance
-        if config.EnableSharing:
+        if config.Sharing.Enabled:
             extra_compliance += customxml.calendarserver_sharing_compliance
         return tuple(super(CalDAVComplianceMixIn, self).davComplianceClasses()) + extra_compliance
 
@@ -421,7 +421,7 @@
         elif property.qname() == (dav_namespace, "resourcetype"):
             if self.isCalendarCollection():
                 sawShare = [child for child in property.children if child.qname() == (calendarserver_namespace, "shared-owner")]
-                if not config.EnableSharing:
+                if not (config.Sharing.Enabled and config.Sharing.Calendars.Enabled):
                     raise HTTPError(StatusResponse(
                         responsecode.FORBIDDEN,
                         "Cannot create shared calendars on this server.",
@@ -1103,7 +1103,7 @@
                 else:
                     returnValue(customxml.DropBoxHomeURL(davxml.HRef(url)))
 
-            elif name == "notification-URL" and config.EnableSharing:
+            elif name == "notification-URL" and config.Sharing.Enabled:
                 url = yield self.notificationURL()
                 if url is None:
                     returnValue(None)

Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/sharing.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/sharing.py	2010-03-04 01:50:42 UTC (rev 5238)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/sharing.py	2010-03-04 16:46:51 UTC (rev 5239)
@@ -13,10 +13,11 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 ##
-from twisted.internet.defer import succeed, inlineCallbacks, DeferredList
+from twisted.internet.defer import succeed, inlineCallbacks, DeferredList,\
+    returnValue
 from twext.web2 import responsecode
 from twext.web2.http import HTTPError, Response
-from twext.web2.dav.http import ErrorResponse
+from twext.web2.dav.http import ErrorResponse, MultiStatusResponse
 from twext.web2.dav.util import allDataFromStream
 from twext.web2.dav.element.base import PCDATAElement
 from twistedcaldav.sql import AbstractSQLDatabase, db_prefix
@@ -48,7 +49,8 @@
         
         # Build the CS:invite property from our DB
         def sharedOK(isShared):
-            if config.EnableSharing and isShared:
+            if config.Sharing.Enabled and isShared:
+                self.validateInvites()
                 return customxml.Invite(
                     *[record.makePropertyElement() for record in self.invitesDB().allRecords()]
                 )
@@ -98,6 +100,41 @@
         """ Return True if this is a shared calendar collection """
         return succeed(self.isSpecialCollection(customxml.Shared))
 
+    def validUserIDForShare(self, userid):
+        """
+        Test the user id to see if it is a valid identifier for sharing and return a "normalized"
+        form for our own use (e.g. convert mailto: to urn:uuid).
+
+        @param userid: the userid to test
+        @type userid: C{str}
+        
+        @return: C{str} of normalized userid or C{None} if
+            userid is not allowed.
+        """
+        
+        # First try to resolve as a principal
+        principal = self.principalForCalendarUserAddress(userid)
+        if principal:
+            return principal.principalURL()
+        
+        # TODO: we do not support external users right now so this is being hard-coded
+        # off in spite of the config option.
+        #elif config.Sharing.AllowExternalUsers:
+        #    return userid
+        else:
+            return None
+
+    def validateInvites(self):
+        """
+        Make sure each userid in an invite is valid - if not re-write status.
+        """
+        
+        records = self.invitesDB().allRecords()
+        for record in records:
+            if self.validUserIDForShare(record.userid) is None and record.state != "INVALID":
+                record.state = "INVALID"
+                self.invitesDB().addOrUpdateRecord(record)
+                
     def removeVirtualShare(self, request):
         """ As user of a shared calendar, unlink this calendar collection """
         return succeed(False) 
@@ -114,6 +151,12 @@
             @param userid: 
             @param ace: Must be one of customxml.ReadWriteAccess or customxml.ReadAccess
         """
+        
+        # Check for valid userid first
+        userid = self.validUserIDForShare(userid)
+        if userid is None:
+            return succeed(False)
+
         # TODO: Check if this collection is shared, and error out if it isn't
         hosturl = self.fp.path
         if type(userid) is not list:
@@ -122,17 +165,29 @@
             commonName = [commonName]
         if type(shareName) is not list:
             shareName = [shareName]
+            
         dl = [self.inviteSingleUserToShare(user, ace, summary, hosturl, request, cn=cn, sn=sn) for user, cn, sn in zip(userid, commonName, shareName)]
-        return DeferredList(dl)
+        return DeferredList(dl).addCallback(lambda _:True)
 
     def uninviteUserToShare(self, userid, ace, request):
         """ Send out in uninvite first, and then remove this user from the share list."""
+        
+        # Do not validate the userid - we want to allow invalid users to be removed because they
+        # may have been valid when added, but no longer valid now. Clients should be able to clear out
+        # anything known to be invalid.
+
         # TODO: Check if this collection is shared, and error out if it isn't
         if type(userid) is not list:
             userid = [userid]
-        return DeferredList([self.uninviteSingleUserFromShare(user, ace, request) for user in userid])
+        return DeferredList([self.uninviteSingleUserFromShare(user, ace, request) for user in userid]).addCallback(lambda _:True)
 
     def inviteUserUpdateToShare(self, userid, aceOLD, aceNEW, summary, request, commonName="", shareName=""):
+
+        # Check for valid userid first
+        userid = self.validUserIDForShare(userid)
+        if userid is None:
+            return succeed(False)
+
         hosturl = self.fp.path
         if type(userid) is not list:
             userid = [userid]
@@ -141,7 +196,7 @@
         if type(shareName) is not list:
             shareName = [shareName]
         dl = [self.inviteSingleUserUpdateToShare(user, aceOLD, aceNEW, summary, hosturl, request, commonName=cn, shareName=sn) for user, cn, sn in zip(userid, commonName, shareName)]
-        return DeferredList(dl)
+        return DeferredList(dl).addCallback(lambda _:True)
 
     def inviteSingleUserToShare(self, userid, ace, summary, hosturl, request, cn="", sn=""):
         
@@ -168,9 +223,6 @@
         return self.inviteSingleUserToShare(userid, aceNEW, summary, hosturl, request, commonName, shareName) 
 
     def xmlPOSTNoAuth(self, encoding, request):
-        def _handleResponse(result):
-            return Response(code=responsecode.OK)
-
         def _handleErrorResponse(error):
             if isinstance(error.value, HTTPError) and hasattr(error.value, "response"):
                 return error.value.response
@@ -259,6 +311,8 @@
                         removeDict[userid] = access
 
                 # Special case removing and adding the same user and treat that as an add
+                okusers = set()
+                badusers = set()
                 sameUseridInRemoveAndSet = [u for u in removeDict.keys() if u in setDict]
                 for u in sameUseridInRemoveAndSet:
                     removeACL = removeDict[u]
@@ -267,12 +321,38 @@
                     del removeDict[u]
                     del setDict[u]
                 for userid, access in removeDict.iteritems():
-                    yield self.uninviteUserToShare(userid, access, request)
+                    result = (yield self.uninviteUserToShare(userid, access, request))
+                    (okusers if result else badusers).add(userid)
                 for userid, (access, summary) in setDict.iteritems():
-                    yield self.inviteUserToShare(userid, access, summary, request)
+                    result = (yield self.inviteUserToShare(userid, access, summary, request))
+                    (okusers if result else badusers).add(userid)
                 for userid, (removeACL, newACL, summary) in updateinviteDict.iteritems():
-                    yield self.inviteUserUpdateToShare(userid, removeACL, newACL, summary, request)
+                    result = (yield self.inviteUserUpdateToShare(userid, removeACL, newACL, summary, request))
+                    (okusers if result else badusers).add(userid)
 
+                # Do a final validation of the entire set of invites
+                self.validateInvites()
+                
+                # Create the multistatus response - only needed if some are bad
+                if badusers:
+                    xml_responses = []
+                    xml_responses.extend([
+                        davxml.StatusResponse(davxml.HRef(userid), davxml.Status.fromResponseCode(responsecode.OK))
+                        for userid in sorted(okusers)
+                    ])
+                    xml_responses.extend([
+                        davxml.StatusResponse(davxml.HRef(userid), davxml.Status.fromResponseCode(responsecode.FORBIDDEN))
+                        for userid in sorted(badusers)
+                    ])
+                
+                    #
+                    # Return response
+                    #
+                    returnValue(MultiStatusResponse(xml_responses))
+                else:
+                    returnValue(responsecode.OK)
+                    
+
             return self.isShared(request).addCallback(_autoShare, request).addCallback(_processInviteDoc, request)
 
         def _getData(data):
@@ -287,7 +367,7 @@
                 customxml.InviteShare: _handleInvite, 
             }
             if type(root) in xmlDocHanders:
-                return xmlDocHanders[type(root)](root).addCallbacks(_handleResponse, errback=_handleErrorResponse)
+                return xmlDocHanders[type(root)](root).addErrback(_handleErrorResponse)
             else:
                 self.log_error("Unsupported XML (%s)" % (root,))
                 raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (customxml.calendarserver_namespace, "valid-request-content")))
@@ -332,13 +412,14 @@
 }
 inviteAccessMapFromXML = dict([(v,k) for k,v in inviteAccessMapToXML.iteritems()])
 
-inviteAcceptMapToXML = {
+inviteStatusMapToXML = {
     "NEEDS-ACTION" : customxml.InviteStatusNoResponse,
     "ACCEPTED"     : customxml.InviteStatusAccepted,
     "DECLINED"     : customxml.InviteStatusDeclined,
     "DELETED"      : customxml.InviteStatusDeleted,
+    "INVALID"      : customxml.InviteStatusInvalid,
 }
-inviteAcceptMapFromXML = dict([(v,k) for k,v in inviteAcceptMapToXML.iteritems()])
+inviteStatusMapFromXML = dict([(v,k) for k,v in inviteStatusMapToXML.iteritems()])
 
 class Invite(object):
     
@@ -355,7 +436,7 @@
             customxml.UID.fromString(self.inviteuid),
             davxml.HRef.fromString(self.userid),
             customxml.InviteAccess(inviteAccessMapToXML[self.access]()),
-            inviteAcceptMapToXML[self.state](),
+            inviteStatusMapToXML[self.state](),
         )
 
 class InvitesDatabase(AbstractSQLDatabase, LoggingMixIn):

Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/static.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/static.py	2010-03-04 01:50:42 UTC (rev 5238)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/static.py	2010-03-04 16:46:51 UTC (rev 5239)
@@ -943,7 +943,7 @@
         else:
             FreeBusyURLFileClass = None
             
-        if config.EnableSharing:
+        if config.Sharing.Enabled:
             NotificationCollectionFileClass = NotificationCollectionFile
         else:
             NotificationCollectionFileClass = None

Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/stdconfig.py	2010-03-04 01:50:42 UTC (rev 5238)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/stdconfig.py	2010-03-04 16:46:51 UTC (rev 5239)
@@ -330,8 +330,20 @@
     "EnableDropBox"           : False, # Calendar Drop Box
     "EnablePrivateEvents"     : False, # Private Events
     "EnableTimezoneService"   : False, # Timezone service
-    "EnableSharing"           : False, # Sharing
+    
+    "Sharing": {
+        "Enabled"             : False, # Overall on/off switch
+        "AllowExternalUsers"  : False, # External (non-principal) sharees allowed
 
+        "Calendars" : {
+            "Enabled"         : False, # Calendar on/off switch
+            "AllowScheduling" : False, # Scheduling in shared calendars
+        },
+        "AddressBooks" : {
+            "Enabled"         : False, # Address Books on/off switch
+        }        
+    },
+
     #
     # Web-based administration
     #
@@ -904,8 +916,8 @@
     # FIXME: Use the config object instead of doing this here
     #
     from twistedcaldav.resource import CalDAVResource, CalendarPrincipalResource
-    CalDAVResource.enableSharing(configDict.EnableSharing)
-    CalendarPrincipalResource.enableSharing(configDict.EnableSharing)
+    CalDAVResource.enableSharing(configDict.Sharing.Enabled)
+    CalendarPrincipalResource.enableSharing(configDict.Sharing.Enabled)
 
 def _updatePartitions(configDict):
     #

Modified: CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/test/test_sharing.py
===================================================================
--- CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/test/test_sharing.py	2010-03-04 01:50:42 UTC (rev 5238)
+++ CalendarServer/branches/users/cdaboo/shared-calendars-5187/twistedcaldav/test/test_sharing.py	2010-03-04 16:46:51 UTC (rev 5239)
@@ -15,39 +15,55 @@
 ##
 
 
+from twext.web2 import responsecode
+from twext.web2.dav import davxml
+from twext.web2.http_headers import MimeType
+from twext.web2.stream import MemoryStream
+from twext.web2.test.test_server import SimpleRequest
+from twisted.internet.defer import inlineCallbacks, returnValue
+from twistedcaldav import customxml
+from twistedcaldav.config import config
+from twistedcaldav.static import CalDAVFile
 from twistedcaldav.test.util import InMemoryPropertyStore
 from twistedcaldav.test.util import TestCase
-from twext.web2.dav import davxml
-from twistedcaldav.static import CalDAVFile
-from twistedcaldav import customxml
-from twisted.internet.defer import inlineCallbacks, returnValue
 import os
-from twistedcaldav.config import config
-from twext.web2.test.test_server import SimpleRequest
-from twext.web2.stream import MemoryStream
-from twext.web2.http_headers import MimeType
-from twext.web2 import responsecode
 
 
 class SharingTests(TestCase):
     def setUp(self):
-        TestCase.setUp(self)
-        config.EnableSharing = True
+        super(SharingTests, self).setUp()
+        config.Sharing.Enabled = True
+        config.Sharing.Calendars.Enabled = True
 
         collection = self.mktemp()
         os.mkdir(collection)
         self.resource = CalDAVFile(collection, self.site.resource)
         self.resource._dead_properties = InMemoryPropertyStore()
         self.site.resource.putChild("calendar", self.resource)
+        
+        self.resource.validUserIDForShare = self._fakeValidUserID
+        
+    def _fakeValidUserID(self, userid):
+        if userid.endswith("@example.com"):
+            return userid
+        else:
+            return None
 
+    def _fakeInvalidUserID(self, userid):
+        if userid.endswith("@example.net"):
+            return userid
+        else:
+            return None
+
     @inlineCallbacks
-    def _doPOST(self, body):
+    def _doPOST(self, body, resultcode = responsecode.OK):
         request = SimpleRequest(self.site, "POST", "/calendar/")
         request.headers.setHeader("content-type", MimeType("text", "xml"))
         request.stream = MemoryStream(body)
 
         response = (yield self.send(request, None))
-        self.assertEqual(response.code, responsecode.OK)
+        self.assertEqual(response.code, resultcode)
+        returnValue(response)
 
     def _clearUIDElementValue(self, xml):
         
@@ -359,3 +375,81 @@
                 customxml.InviteStatusNoResponse(),
             ),
         ))
+
+    @inlineCallbacks
+    def test_POSTaddInvalidInvitee(self):
+        
+        yield self.resource.upgradeToShare(None)
+
+        response = (yield self._doPOST("""<?xml version="1.0" encoding="utf-8" ?>
+<CS:share xmlns:D="DAV:" xmlns:CS="http://calendarserver.org/ns/">
+    <CS:set>
+        <D:href>mailto:bogus at example.net</D:href>
+        <CS:summary>My Shared Calendar</CS:summary>
+        <CS:read-write/>
+    </CS:set>
+</CS:share>
+""",
+            responsecode.MULTI_STATUS
+        ))
+        
+        self.assertEqual(
+            str(response.stream.read()).replace("\r\n", "\n"),
+            """<?xml version='1.0' encoding='UTF-8'?><multistatus xmlns='DAV:'>
+  <response>
+    <href>mailto:bogus at example.net</href>
+    <status>HTTP/1.1 403 Forbidden</status>
+  </response>
+</multistatus>"""
+        )
+
+        propInvite = (yield self.resource.readProperty(customxml.Invite, None))
+        self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite())
+
+    @inlineCallbacks
+    def test_POSTremoveInvalidInvitee(self):
+        
+        yield self.resource.upgradeToShare(None)
+
+        yield self._doPOST("""<?xml version="1.0" encoding="utf-8" ?>
+<CS:share xmlns:D="DAV:" xmlns:CS="http://calendarserver.org/ns/">
+    <CS:set>
+        <D:href>mailto:user01 at example.com</D:href>
+        <CS:summary>My Shared Calendar</CS:summary>
+        <CS:read-write/>
+    </CS:set>
+</CS:share>
+""")
+
+        propInvite = (yield self.resource.readProperty(customxml.Invite, None))
+        self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
+            customxml.InviteUser(
+                customxml.UID.fromString(""),
+                davxml.HRef.fromString("mailto:user01 at example.com"),
+                customxml.InviteAccess(customxml.ReadWriteAccess()),
+                customxml.InviteStatusNoResponse(),
+            )
+        ))
+
+        self.resource.validUserIDForShare = self._fakeInvalidUserID
+
+        propInvite = (yield self.resource.readProperty(customxml.Invite, None))
+        self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite(
+            customxml.InviteUser(
+                customxml.UID.fromString(""),
+                davxml.HRef.fromString("mailto:user01 at example.com"),
+                customxml.InviteAccess(customxml.ReadWriteAccess()),
+                customxml.InviteStatusInvalid(),
+            )
+        ))
+        
+        yield self._doPOST("""<?xml version="1.0" encoding="utf-8" ?>
+<CS:share xmlns:D="DAV:" xmlns:CS="http://calendarserver.org/ns/">
+    <CS:remove>
+        <D:href>mailto:user01 at example.com</D:href>
+    </CS:remove>
+</CS:share>
+""")
+
+        propInvite = (yield self.resource.readProperty(customxml.Invite, None))
+        self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite())
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20100304/0265edd4/attachment-0001.html>


More information about the calendarserver-changes mailing list