[CalendarServer-changes] [9818] CalendarServer/trunk/twistedcaldav

source_changes at macosforge.org source_changes at macosforge.org
Tue Sep 18 09:57:19 PDT 2012


Revision: 9818
          http://trac.calendarserver.org//changeset/9818
Author:   cdaboo at apple.com
Date:     2012-09-18 09:57:19 -0700 (Tue, 18 Sep 2012)
Log Message:
-----------
Prevent attempt to invite self to a shared calendar.

Modified Paths:
--------------
    CalendarServer/trunk/twistedcaldav/sharing.py
    CalendarServer/trunk/twistedcaldav/test/test_sharing.py

Modified: CalendarServer/trunk/twistedcaldav/sharing.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/sharing.py	2012-09-18 16:46:06 UTC (rev 9817)
+++ CalendarServer/trunk/twistedcaldav/sharing.py	2012-09-18 16:57:19 UTC (rev 9818)
@@ -32,7 +32,7 @@
 from twext.web2.dav.util import allDataFromStream, joinURL
 from txdav.xml import element
 
-from twisted.internet.defer import succeed, inlineCallbacks, DeferredList,\
+from twisted.internet.defer import succeed, inlineCallbacks, DeferredList, \
     returnValue
 
 from twistedcaldav import customxml, caldavxml
@@ -62,20 +62,20 @@
         invites database.
         """
         if config.Sharing.Enabled:
-            
+
             # See if this property is on the shared calendar
             isShared = yield self.isShared(request)
             if isShared:
-                yield self.validateInvites()
+                yield self.validateInvites(request)
                 records = yield self.invitesDB().allRecords()
                 returnValue(customxml.Invite(
                     *[record.makePropertyElement() for record in records]
                 ))
-                
+
             # See if it is on the sharee calendar
             if self.isVirtualShare():
                 original = (yield request.locateResource(self._share.hosturl))
-                yield original.validateInvites()
+                yield original.validateInvites(request)
                 records = yield original.invitesDB().allRecords()
 
                 ownerPrincipal = (yield original.ownerPrincipal(request))
@@ -95,24 +95,25 @@
 
     def upgradeToShare(self):
         """ Upgrade this collection to a shared state """
-        
+
         # Change resourcetype
         rtype = self.resourceType()
         rtype = element.ResourceType(*(rtype.children + (customxml.SharedOwner(),)))
         self.writeDeadProperty(rtype)
-        
+
         # Create invites database
         self.invitesDB().create()
-    
+
+
     @inlineCallbacks
     def downgradeFromShare(self, request):
-        
+
         # Change resource type (note this might be called after deleting a resource
         # so we have to cope with that)
         rtype = self.resourceType()
         rtype = element.ResourceType(*([child for child in rtype.children if child != customxml.SharedOwner()]))
         self.writeDeadProperty(rtype)
-        
+
         # Remove all invitees
         for record in (yield self.invitesDB().allRecords()):
             yield self.uninviteRecordFromShare(record, request)
@@ -120,7 +121,7 @@
         # Remove invites database
         self.invitesDB().remove()
         delattr(self, "_invitesDB")
-    
+
         returnValue(True)
 
 
@@ -133,7 +134,7 @@
                 (customxml.calendarserver_namespace, "valid-request"),
                 "Invalid share",
             ))
-        
+
         principalUID = principalURL.split("/")[3]
         record = yield self.invitesDB().recordForInviteUID(inviteUID)
         if record is None or record.principalUID != principalUID:
@@ -142,7 +143,7 @@
                 (customxml.calendarserver_namespace, "valid-request"),
                 "Invalid invitation uid: %s" % (inviteUID,),
             ))
-        
+
         # Only certain states are sharer controlled
         if record.state in ("NEEDS-ACTION", "ACCEPTED", "DECLINED",):
             record.state = state
@@ -160,10 +161,10 @@
         @param request: the request triggering this action
         @type request: L{IRequest}
         """
-        
+
         # Need to have at least DAV:read to do this
         yield self.authorize(request, (element.Read(),))
-        
+
         # Find current principal
         authz_principal = self.currentPrincipal(request).children[0]
         if not isinstance(authz_principal, element.HRef):
@@ -180,7 +181,7 @@
                 "Current user principal not specified",
             ))
         principal = (yield request.locateResource(principalURL))
-        
+
         # Check enabled for service
         from twistedcaldav.directory.principal import DirectoryCalendarPrincipalResource
         if not isinstance(principal, DirectoryCalendarPrincipalResource):
@@ -189,7 +190,7 @@
                 (calendarserver_namespace, "invalid-principal"),
                 "Current user principal is not a calendar/addressbook enabled principal",
             ))
-        
+
         # Get the home collection
         if self.isCalendarCollection():
             home = yield principal.calendarHome(request)
@@ -201,7 +202,7 @@
                 (calendarserver_namespace, "invalid-principal"),
                 "No calendar/addressbook home for principal",
             ))
-            
+
         # TODO: Make sure principal is not sharing back to themselves
         compareURL = (yield self.canonicalURL(request))
         homeURL = home.url()
@@ -243,7 +244,7 @@
     @inlineCallbacks
     def removeVirtualShare(self, request):
         """ Return True if this is a shared calendar collection """
-        
+
         # Remove from sharee's calendar/address book home
         if self.isCalendarCollection():
             shareeHome = yield self._shareePrincipal.calendarHome(request)
@@ -276,7 +277,7 @@
         """
         Return the DAV:resourcetype stripped of any shared elements.
         """
-        
+
         if self.isCalendarCollection():
             return "calendar"
         elif self.isAddressBookCollection():
@@ -314,7 +315,7 @@
                 returnValue(result)
         else:
             # Invite shares use access mode from the invite
-    
+
             # Get the invite for this sharee
             invite = yield self.invitesDB().recordForInviteUID(
                 self._share.shareuid
@@ -322,7 +323,7 @@
             if invite is None:
                 returnValue(element.ACL())
             inviteAccess = invite.access
-            
+
         userprivs = [
         ]
         if inviteAccess in ("read-only", "read-write", "read-write-schedule",):
@@ -389,7 +390,9 @@
 
         returnValue(element.ACL(*aces))
 
-    def validUserIDForShare(self, userid):
+
+    @inlineCallbacks
+    def validUserIDForShare(self, userid, request):
         """
         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
@@ -397,23 +400,28 @@
 
         @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()
-        
+            ownerPrincipal = (yield self.ownerPrincipal(request))
+            owner = ownerPrincipal.principalURL()
+            if owner == principal.principalURL():
+                returnValue(None)
+            returnValue(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
+            returnValue(None)
 
+
     def validUserIDWithCommonNameForShare(self, userid, cn):
         """
         Validate user ID and find the common name.
@@ -422,16 +430,16 @@
         @type userid: C{str}
         @param cn: default common name to use if principal has none
         @type cn: C{str}
-        
+
         @return: C{tuple} of C{str} of normalized userid or C{None} if
             userid is not allowed, and appropriate common name.
         """
-        
+
         # First try to resolve as a principal
         principal = self.principalForCalendarUserAddress(userid)
         if principal:
             return userid, principal.principalURL(), principal.displayName()
-        
+
         # 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:
@@ -441,24 +449,25 @@
 
 
     @inlineCallbacks
-    def validateInvites(self):
+    def validateInvites(self, request):
         """
         Make sure each userid in an invite is valid - if not re-write status.
         """
-        
+
         records = yield self.invitesDB().allRecords()
         for record in records:
-            if self.validUserIDForShare(record.userid) is None and record.state != "INVALID":
+            uid = (yield self.validUserIDForShare(record.userid, request))
+            if uid is None and record.state != "INVALID":
                 record.state = "INVALID"
                 yield self.invitesDB().addOrUpdateRecord(record)
 
 
     def inviteUserToShare(self, userid, cn, ace, summary, request):
         """ Send out in invite first, and then add this user to the share list
-            @param userid: 
+            @param userid:
             @param ace: Must be one of customxml.ReadWriteAccess or customxml.ReadAccess
         """
-        
+
         # TODO: Check if this collection is shared, and error out if it isn't
         resultIsList = True
         if type(userid) is not list:
@@ -466,13 +475,14 @@
             resultIsList = False
         if type(cn) is not list:
             cn = [cn]
-            
+
         dl = [self.inviteSingleUserToShare(user, cn, ace, summary, request) for user, cn in zip(userid, cn)]
         return self._processShareActionList(dl, resultIsList)
 
+
     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.
@@ -486,6 +496,7 @@
         dl = [self.uninviteSingleUserFromShare(user, ace, request) for user in userid]
         return self._processShareActionList(dl, resultIsList)
 
+
     def inviteUserUpdateToShare(self, userid, cn, aceOLD, aceNEW, summary, request):
 
         resultIsList = True
@@ -494,17 +505,18 @@
             resultIsList = False
         if type(cn) is not list:
             cn = [cn]
-            
+
         dl = [self.inviteSingleUserUpdateToShare(user, cn, aceOLD, aceNEW, summary, request) for user, cn in zip(userid, cn)]
         return self._processShareActionList(dl, resultIsList)
 
+
     def _processShareActionList(self, dl, resultIsList):
         def _defer(resultset):
             results = [result if success else False for success, result in resultset]
             return results if resultIsList else results[0]
         return DeferredList(dl).addCallback(_defer)
-        
 
+
     @inlineCallbacks
     def _createLock(self, userid, request):
         """
@@ -518,6 +530,7 @@
             expire_time=config.Scheduling.Options.UIDLockExpirySeconds,
         ))
 
+
     @inlineCallbacks
     def _acquireLock(self, lock):
         """
@@ -529,6 +542,7 @@
             self.log_error("Memcache lock timeout for sharing invite")
             raise
 
+
     @inlineCallbacks
     def _lockToken(self, userid, request):
         """
@@ -537,12 +551,13 @@
         hosturl = (yield self.canonicalURL(request))
         returnValue("%s:%s" % (hosturl, userid))
 
+
     @inlineCallbacks
     def inviteSingleUserToShare(self, userid, cn, ace, summary, request):
-        
+
         # Validate userid and cn
         userid, principalURL, cn = self.validUserIDWithCommonNameForShare(userid, cn)
-        
+
         # We currently only handle local users
         if principalURL is None:
             returnValue(False)
@@ -600,7 +615,7 @@
 
     @inlineCallbacks
     def uninviteRecordFromShare(self, record, request):
-        
+
         # Remove any shared calendar or address book
         sharee = self.principalForCalendarUserAddress(record.userid)
         if sharee:
@@ -609,7 +624,7 @@
             elif self.isAddressBookCollection():
                 shareeHome = yield sharee.addressBookHome(request)
             yield shareeHome.removeShareByUID(request, record.inviteuid)
-    
+
             # If current user state is accepted then we send an invite with the new state, otherwise
             # we cancel any existing invites for the user
             if record and record.state != "ACCEPTED":
@@ -617,20 +632,22 @@
             elif record:
                 record.state = "DELETED"
                 yield self.sendInvite(record, request)
-    
+
         # Remove from database
         yield self.invitesDB().removeRecordForInviteUID(record.inviteuid)
-        
-        returnValue(True)            
 
+        returnValue(True)
+
+
     def inviteSingleUserUpdateToShare(self, userid, commonName, acesOLD, aceNEW, summary, request):
-        
+
         # Just update existing
-        return self.inviteSingleUserToShare(userid, commonName, aceNEW, summary, request) 
+        return self.inviteSingleUserToShare(userid, commonName, aceNEW, summary, request)
 
+
     @inlineCallbacks
     def sendInvite(self, record, request):
-        
+
         ownerPrincipal = (yield self.ownerPrincipal(request))
         owner = ownerPrincipal.principalURL()
         ownerCN = ownerPrincipal.displayName()
@@ -640,19 +657,19 @@
         sharee = self.principalForCalendarUserAddress(record.userid)
         if sharee is None:
             raise ValueError("sharee is None but userid was valid before")
-        
+
         # We need to look up the resource so that the response cache notifier is properly initialized
         notificationResource = (yield request.locateResource(sharee.notificationURL()))
         notifications = notificationResource._newStoreNotifications
-        
+
         # Look for existing notification
         oldnotification = (yield notifications.notificationObjectWithUID(record.inviteuid))
         if oldnotification:
             # TODO: rollup changes?
             pass
-        
+
         # Generate invite XML
-        typeAttr = {'shared-type':self.sharedResourceType()}
+        typeAttr = {'shared-type': self.sharedResourceType()}
         xmltype = customxml.InviteNotification(**typeAttr)
         xmldata = customxml.Notification(
             customxml.DTStamp.fromString(PyCalendarDateTime.getNowUTC().getText()),
@@ -673,28 +690,31 @@
                 **typeAttr
             ),
         ).toxml()
-        
+
         # Add to collections
         yield notifications.writeNotificationObject(record.inviteuid, xmltype, xmldata)
 
+
     @inlineCallbacks
     def removeInvite(self, record, request):
-        
+
         # Locate notifications collection for user
         sharee = self.principalForCalendarUserAddress(record.userid)
         if sharee is None:
             raise ValueError("sharee is None but userid was valid before")
         notifications = (yield request.locateResource(sharee.notificationURL()))
-        
+
         # Add to collections
         yield notifications.deleteNotifictionMessageByUID(request, record.inviteuid)
 
+
     @inlineCallbacks
     def _xmlHandleInvite(self, request, docroot):
         yield self.authorize(request, (element.Read(), element.Write()))
         result = (yield self._handleInvite(request, docroot))
         returnValue(result)
-    
+
+
     def _handleInvite(self, request, invitedoc):
         def _handleInviteSet(inviteset):
             userid = None
@@ -765,13 +785,14 @@
                 if isinstance(item, customxml.InviteSet):
                     userid, cn, access, summary = _handleInviteSet(item)
                     setDict[userid] = (cn, access, summary)
-                
+
                     # Validate each userid on add only
-                    (okusers if self.validUserIDForShare(userid) else badusers).add(userid)
+                    uid = (yield self.validUserIDForShare(userid, request))
+                    (okusers if uid is not None else badusers).add(userid)
                 elif isinstance(item, customxml.InviteRemove):
                     userid, access = _handleInviteRemove(item)
                     removeDict[userid] = access
-                    
+
                     # Treat removed userids as valid as we will fail invalid ones silently
                     okusers.add(userid)
 
@@ -806,8 +827,8 @@
                 ok_code = responsecode.FAILED_DEPENDENCY
 
             # Do a final validation of the entire set of invites
-            yield self.validateInvites()
-            
+            yield self.validateInvites(request)
+
             # Create the multistatus response - only needed if some are bad
             if badusers:
                 xml_responses = []
@@ -819,7 +840,7 @@
                     element.StatusResponse(element.HRef(userid), element.Status.fromResponseCode(responsecode.FORBIDDEN))
                     for userid in sorted(badusers)
                 ])
-            
+
                 #
                 # Return response
                 #
@@ -829,18 +850,21 @@
 
         return self.isShared(request).addCallback(_autoShare, request).addCallback(_processInviteDoc, request)
 
+
     @inlineCallbacks
     def _xmlHandleInviteReply(self, request, docroot):
         yield self.authorize(request, (element.Read(), element.Write()))
         result = (yield self._handleInviteReply(request, docroot))
         returnValue(result)
-    
+
+
     def _handleInviteReply(self, request, docroot):
         raise NotImplementedError
 
+
     @inlineCallbacks
     def xmlRequestHandler(self, request):
-        
+
         # Need to read the data and get the root element first
         xmldata = (yield allDataFromStream(request.stream))
         try:
@@ -867,7 +891,7 @@
 
     xmlDocHandlers = {
         customxml.InviteShare: _xmlHandleInvite,
-        customxml.InviteReply: _xmlHandleInviteReply,          
+        customxml.InviteReply: _xmlHandleInviteReply,
     }
 
     def POST_handler_content_type(self, request, contentType):
@@ -890,7 +914,7 @@
     "read-only"           : customxml.ReadAccess,
     "read-write"          : customxml.ReadWriteAccess,
 }
-inviteAccessMapFromXML = dict([(v,k) for k,v in inviteAccessMapToXML.iteritems()])
+inviteAccessMapFromXML = dict([(v, k) for k, v in inviteAccessMapToXML.iteritems()])
 
 inviteStatusMapToXML = {
     "NEEDS-ACTION" : customxml.InviteStatusNoResponse,
@@ -899,10 +923,10 @@
     "DELETED"      : customxml.InviteStatusDeleted,
     "INVALID"      : customxml.InviteStatusInvalid,
 }
-inviteStatusMapFromXML = dict([(v,k) for k,v in inviteStatusMapToXML.iteritems()])
+inviteStatusMapFromXML = dict([(v, k) for k, v in inviteStatusMapToXML.iteritems()])
 
 class Invite(object):
-    
+
     def __init__(self, inviteuid, userid, principalUID, common_name, access, state, summary):
         self.inviteuid = inviteuid
         self.userid = userid
@@ -911,9 +935,10 @@
         self.access = access
         self.state = state
         self.summary = summary
-        
+
+
     def makePropertyElement(self, includeUID=True):
-        
+
         return customxml.InviteUser(
             customxml.UID.fromString(self.inviteuid) if includeUID else None,
             element.HRef.fromString(self.userid),
@@ -922,8 +947,10 @@
             inviteStatusMapToXML[self.state](),
         )
 
+
+
 class InvitesDatabase(AbstractSQLDatabase, LoggingMixIn):
-    
+
     db_basename = db_prefix + "invites"
     schema_version = "1"
     db_type = "invites"
@@ -937,6 +964,7 @@
         db_filename = os.path.join(self.resource.fp.path, InvitesDatabase.db_basename)
         super(InvitesDatabase, self).__init__(db_filename, True, autocommit=True)
 
+
     def create(self):
         """
         Create the index and initialize it.
@@ -954,53 +982,62 @@
     dbpath = property(get_dbpath, set_dbpath)
 
     def allRecords(self):
-        
+
         records = self._db_execute("select * from INVITE order by USERID")
         return [self._makeRecord(row) for row in (records if records is not None else ())]
-    
+
+
     def recordForUserID(self, userid):
-        
+
         row = self._db_execute("select * from INVITE where USERID = :1", userid)
         return self._makeRecord(row[0]) if row else None
-    
+
+
     def recordForPrincipalUID(self, principalUID):
-        
+
         row = self._db_execute("select * from INVITE where PRINCIPALUID = :1", principalUID)
         return self._makeRecord(row[0]) if row else None
-    
+
+
     def recordForInviteUID(self, inviteUID):
 
         row = self._db_execute("select * from INVITE where INVITEUID = :1", inviteUID)
         return self._makeRecord(row[0]) if row else None
-    
+
+
     def addOrUpdateRecord(self, record):
 
         self._db_execute("""insert or replace into INVITE (INVITEUID, USERID, PRINCIPALUID, NAME, ACCESS, STATE, SUMMARY)
             values (:1, :2, :3, :4, :5, :6, :7)
             """, record.inviteuid, record.userid, record.principalUID, record.name, record.access, record.state, record.summary,
         )
-    
+
+
     def removeRecordForInviteUID(self, inviteUID):
 
         self._db_execute("delete from INVITE where INVITEUID = :1", inviteUID)
-    
+
+
     def remove(self):
-        
+
         self._db_close()
         os.remove(self.dbpath)
 
+
     def _db_version(self):
         """
         @return: the schema version assigned to this index.
         """
         return InvitesDatabase.schema_version
 
+
     def _db_type(self):
         """
         @return: the collection type assigned to this index.
         """
         return InvitesDatabase.db_type
 
+
     def _db_init_data_tables(self, q):
         """
         Initialise the underlying database tables.
@@ -1046,6 +1083,7 @@
             """
         )
 
+
     def _db_upgrade_data_tables(self, q, old_version):
         """
         Upgrade the data from an older version of the DB.
@@ -1054,17 +1092,19 @@
         # Nothing to do as we have not changed the schema
         pass
 
+
     def _makeRecord(self, row):
-        
+
         return Invite(*[str(item) if type(item) == types.UnicodeType else item for item in row])
 
+
+
 class SharedHomeMixin(LinkFollowerMixIn):
     """
     A mix-in for calendar/addressbook homes that defines the operations for
     manipulating a sharee's set of shared calendars.
     """
 
-
     @inlineCallbacks
     def provisionShare(self, name):
         # Try to find a matching share
@@ -1094,7 +1134,7 @@
 
     @inlineCallbacks
     def acceptInviteShare(self, request, hostUrl, inviteUID, displayname=None):
-        
+
         # Check for old share
         oldShare = yield self.sharesDB().recordForShareUID(inviteUID)
 
@@ -1104,6 +1144,7 @@
         response = (yield self._acceptShare(request, oldShare, SHARETYPE_INVITE, hostUrl, inviteUID, displayname))
         returnValue(response)
 
+
     @inlineCallbacks
     def acceptDirectShare(self, request, hostUrl, resourceUID, displayname=None):
 
@@ -1112,6 +1153,7 @@
         response = (yield self._acceptShare(request, oldShare, SHARETYPE_DIRECT, hostUrl, resourceUID, displayname))
         returnValue(response)
 
+
     @inlineCallbacks
     def _acceptShare(self, request, oldShare, sharetype, hostUrl, shareUID, displayname=None):
 
@@ -1121,7 +1163,7 @@
         else:
             share = SharedCollectionRecord(shareUID, sharetype, hostUrl, str(uuid4()), displayname)
             yield self.sharesDB().addOrUpdateRecord(share)
-        
+
         # Get shared collection in non-share mode first
         sharedCollection = (yield request.locateResource(hostUrl))
         ownerPrincipal = (yield self.ownerPrincipal(request))
@@ -1133,7 +1175,7 @@
                 color = (yield sharedCollection.readProperty(customxml.CalendarColor, request))
             except HTTPError:
                 pass
-        
+
         # Set per-user displayname or color to whatever was given
         sharedCollection.setVirtualShare(ownerPrincipal, share)
         if displayname:
@@ -1148,18 +1190,19 @@
             yield sharedCollection.writeProperty(caldavxml.DefaultAlarmVEventDate.fromString(""), request)
             yield sharedCollection.writeProperty(caldavxml.DefaultAlarmVToDoDateTime.fromString(""), request)
             yield sharedCollection.writeProperty(caldavxml.DefaultAlarmVToDoDate.fromString(""), request)
- 
+
         # Notify client of changes
         yield self.notifyChanged()
 
         # Return the URL of the shared collection
         returnValue(XMLResponse(
-            code = responsecode.OK,
-            element = customxml.SharedAs(
+            code=responsecode.OK,
+            element=customxml.SharedAs(
                 element.HRef.fromString(joinURL(self.url(), share.localname))
             )
         ))
 
+
     def removeShare(self, request, share):
         """ Remove a shared collection named in resourceName """
 
@@ -1169,6 +1212,7 @@
         else:
             return self.removeDirectShare(request, share)
 
+
     @inlineCallbacks
     def removeShareByUID(self, request, shareUID):
         """ Remove a shared collection but do not send a decline back """
@@ -1179,6 +1223,7 @@
 
         returnValue(True)
 
+
     @inlineCallbacks
     def removeDirectShare(self, request, share):
         """ Remove a shared collection but do not send a decline back """
@@ -1194,10 +1239,11 @@
                 inbox.processFreeBusyCalendar(shareURL, False)
 
         yield self.sharesDB().removeRecordForShareUID(share.shareuid)
- 
+
         # Notify client of changes
         yield self.notifyChanged()
 
+
     @inlineCallbacks
     def declineShare(self, request, hostUrl, inviteUID):
 
@@ -1205,15 +1251,16 @@
         yield self.removeShareByUID(request, inviteUID)
 
         yield self._changeShare(request, "DECLINED", hostUrl, inviteUID)
-        
+
         returnValue(Response(code=responsecode.NO_CONTENT))
 
+
     @inlineCallbacks
     def _changeShare(self, request, state, hostUrl, replytoUID, displayname=None):
         """
         Accept or decline an invite to a shared collection.
         """
-        
+
         # Change state in sharer invite
         ownerPrincipal = (yield self.ownerPrincipal(request))
         owner = ownerPrincipal.principalURL()
@@ -1225,12 +1272,13 @@
                 (customxml.calendarserver_namespace, "valid-request"),
                 "Invalid shared collection",
             ))
-            
+
         # Change the record
         yield sharedCollection.changeUserInviteState(request, replytoUID, owner, state, displayname)
 
         yield self.sendReply(request, ownerPrincipal, sharedCollection, state, hostUrl, replytoUID, displayname)
 
+
     @inlineCallbacks
     def sendReply(self, request, shareePrincipal, sharedCollection, state, hostUrl, replytoUID, displayname=None):
 
@@ -1270,10 +1318,11 @@
                 )
             ),
         ).toxml()
-        
+
         # Add to collections
         yield notifications.addNotification(request, notificationUID, xmltype, xmldata)
 
+
     def _handleInviteReply(self, request, invitereplydoc):
         """ Handle a user accepting or declining a sharing invite """
         hostUrl = None
@@ -1293,7 +1342,7 @@
                         hostUrl = str(hosturlItem)
             elif isinstance(item, customxml.InReplyTo):
                 replytoUID = str(item)
-        
+
         if accepted is None or hostUrl is None or replytoUID is None:
             raise HTTPError(ErrorResponse(
                 responsecode.FORBIDDEN,
@@ -1305,8 +1354,10 @@
         else:
             return self.declineShare(request, hostUrl, replytoUID)
 
+
+
 class SharedCollectionRecord(object):
-    
+
     def __init__(self, shareuid, sharetype, hosturl, localname, summary):
         self.shareuid = shareuid
         self.sharetype = sharetype
@@ -1314,13 +1365,15 @@
         self.localname = localname
         self.summary = summary
 
+
+
 class SharedCollectionsDatabase(AbstractSQLDatabase, LoggingMixIn):
-    
+
     db_basename = db_prefix + "shares"
     schema_version = "1"
     db_type = "shares"
 
-    def __init__(self, resource):   
+    def __init__(self, resource):
         """
         @param resource: the L{CalDAVResource} resource for
             the shared collection. C{resource} must be a calendar/addressbook home collection.)
@@ -1337,7 +1390,6 @@
     def set_dbpath(self, newpath):
         pass
 
-
     dbpath = property(get_dbpath, set_dbpath)
 
 
@@ -1347,8 +1399,9 @@
         """
         self._db()
 
+
     def allRecords(self):
-        
+
         records = self._db_execute("select * from SHARES order by LOCALNAME")
         return [self._makeRecord(row) for row in (records if records is not None else ())]
 
@@ -1357,42 +1410,50 @@
 
         row = self._db_execute("select * from SHARES where SHAREUID = :1", shareUID)
         return self._makeRecord(row[0]) if row else None
-    
+
+
     def addOrUpdateRecord(self, record):
 
         self._db_execute("""insert or replace into SHARES (SHAREUID, SHARETYPE, HOSTURL, LOCALNAME, SUMMARY)
             values (:1, :2, :3, :4, :5)
             """, record.shareuid, record.sharetype, record.hosturl, record.localname, record.summary,
         )
-    
+
+
     def removeRecordForLocalName(self, localname):
 
         self._db_execute("delete from SHARES where LOCALNAME = :1", localname)
-    
+
+
     def removeRecordForShareUID(self, shareUID):
 
         self._db_execute("delete from SHARES where SHAREUID = :1", shareUID)
-    
+
+
     def remove(self):
-        
+
         self._db_close()
         os.remove(self.dbpath)
 
+
     def directShareID(self, shareeHome, sharerCollection):
         return "Direct-%s-%s" % (shareeHome.resourceID(), sharerCollection.resourceID(),)
 
+
     def _db_version(self):
         """
         @return: the schema version assigned to this index.
         """
         return SharedCollectionsDatabase.schema_version
 
+
     def _db_type(self):
         """
         @return: the collection type assigned to this index.
         """
         return SharedCollectionsDatabase.db_type
 
+
     def _db_init_data_tables(self, q):
         """
         Initialise the underlying database tables.
@@ -1434,6 +1495,7 @@
             """
         )
 
+
     def _db_upgrade_data_tables(self, q, old_version):
         """
         Upgrade the data from an older version of the DB.
@@ -1442,6 +1504,7 @@
         # Nothing to do as we have not changed the schema
         pass
 
+
     def _makeRecord(self, row):
-        
+
         return SharedCollectionRecord(*[str(item) if type(item) == types.UnicodeType else item for item in row])

Modified: CalendarServer/trunk/twistedcaldav/test/test_sharing.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_sharing.py	2012-09-18 16:46:06 UTC (rev 9817)
+++ CalendarServer/trunk/twistedcaldav/test/test_sharing.py	2012-09-18 16:57:19 UTC (rev 9818)
@@ -72,7 +72,7 @@
         self.patch(config.Sharing.Calendars, "Enabled", True)
 
         CalDAVResource.validUserIDForShare = self._fakeValidUserID
-        CalDAVResource.validUserIDWithCommonNameForShare = self._fakeValidUserID
+        CalDAVResource.validUserIDWithCommonNameForShare = self._fakeValidUserID_CN
         CalDAVResource.sendInvite = lambda self, record, request: succeed(True)
         CalDAVResource.removeInvite = lambda self, record, request: succeed(True)
 
@@ -90,7 +90,7 @@
         returnValue(result)
 
 
-    def _fakeValidUserID(self, userid, *args):
+    def _fakeValidUserID_Base(self, userid, request, *args):
         if userid.startswith("/principals/"):
             return userid
         if userid.endswith("@example.com"):
@@ -99,9 +99,27 @@
         else:
             return None if len(args) == 0 else (None, None, None,)
 
-    def _fakeInvalidUserID(self, userid, *args):
+
+    def _fakeValidUserID(self, userid, request, *args):
+        return succeed(self._fakeValidUserID_Base(userid, request, *args))
+
+
+    def _fakeValidUserID_CN(self, userid, *args):
+        return self._fakeValidUserID_Base(userid, None, *args)
+
+
+    def _fakeInvalidUserID_Base(self, userid, request, *args):
         return None if len(args) == 0 else (None, None, None,)
 
+
+    def _fakeInvalidUserID(self, userid, request, *args):
+        return succeed(self._fakeInvalidUserID_Base(userid, request, *args))
+
+
+    def _fakeInvalidUserID_CN(self, userid, *args):
+        return self._fakeInvalidUserID_Base(userid, None, *args)
+
+
     @inlineCallbacks
     def _doPOST(self, body, resultcode = responsecode.OK):
         request = SimpleRequest(self.site, "POST", "/calendar/")
@@ -565,7 +583,7 @@
         ))
 
         self.resource.validUserIDForShare = self._fakeInvalidUserID
-        self.resource.validUserIDWithCommonNameForShare = self._fakeInvalidUserID
+        self.resource.validUserIDWithCommonNameForShare = self._fakeInvalidUserID_CN
         self.resource.principalForCalendarUserAddress = lambda cuaddr: None
 
         propInvite = (yield self.resource.readProperty(customxml.Invite, None))
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20120918/92271ffb/attachment-0001.html>


More information about the calendarserver-changes mailing list