[CalendarServer-changes] [7987] CalendarServer/trunk/twistedcaldav/scheduling/implicit.py

source_changes at macosforge.org source_changes at macosforge.org
Mon Aug 15 07:57:06 PDT 2011


Revision: 7987
          http://trac.macosforge.org/projects/calendarserver/changeset/7987
Author:   cdaboo at apple.com
Date:     2011-08-15 07:57:06 -0700 (Mon, 15 Aug 2011)
Log Message:
-----------
Make sure Organizer cannot make unauthorized changes to Attendee PARTSTAT.

Modified Paths:
--------------
    CalendarServer/trunk/twistedcaldav/scheduling/implicit.py

Modified: CalendarServer/trunk/twistedcaldav/scheduling/implicit.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/scheduling/implicit.py	2011-08-15 14:55:57 UTC (rev 7986)
+++ CalendarServer/trunk/twistedcaldav/scheduling/implicit.py	2011-08-15 14:57:06 UTC (rev 7987)
@@ -27,7 +27,8 @@
 from twistedcaldav.ical import Property
 from twistedcaldav.scheduling import addressmapping
 from twistedcaldav.scheduling.cuaddress import InvalidCalendarUser,\
-    LocalCalendarUser, PartitionedCalendarUser, OtherServerCalendarUser
+    LocalCalendarUser, PartitionedCalendarUser, OtherServerCalendarUser,\
+    normalizeCUAddr
 from twistedcaldav.scheduling.icaldiff import iCalDiff
 from twistedcaldav.scheduling.itip import iTipGenerator, iTIPRequestStatus
 from twistedcaldav.scheduling.scheduler import CalDAVScheduler
@@ -279,7 +280,7 @@
         self.request.suppressRefresh = False
 
         for attendee in self.calendar.getAllAttendeeProperties():
-            if attendee.parameterValue("PARTSTAT", "NEEDS-ACTION") == "NEEDS-ACTION":
+            if attendee.parameterValue("PARTSTAT", "NEEDS-ACTION").upper() == "NEEDS-ACTION":
                 self.request.suppressRefresh = True
         
         if hasattr(self.request, "doing_attendee_refresh"):
@@ -468,6 +469,8 @@
 
             # Read in existing data
             self.oldcalendar = (yield self.resource.iCalendarForUser(self.request))
+            self.oldAttendeesByInstance = self.oldcalendar.getAttendeesByInstance(True, onlyScheduleAgentServer=True)
+            self.coerceAttendeesPartstatOnModify()
             
             # Significant change
             no_change, self.changed_rids, self.needs_action_rids, reinvites, recurrence_reschedule = self.isOrganizerChangeInsignificant()
@@ -503,10 +506,11 @@
 
         elif self.action == "create":
             log.debug("Implicit - organizer '%s' is creating UID: '%s'" % (self.organizer, self.uid))
+            self.coerceAttendeesPartstatOnCreate()
             
         # Always set RSVP=TRUE for any NEEDS-ACTION
         for attendee in self.calendar.getAllAttendeeProperties():
-            if attendee.parameterValue("PARTSTAT", "NEEDS-ACTION") == "NEEDS-ACTION":
+            if attendee.parameterValue("PARTSTAT", "NEEDS-ACTION").upper() == "NEEDS-ACTION":
                 attendee.setParameter("RSVP", "TRUE")
 
         yield self.scheduleWithAttendees()
@@ -602,7 +606,7 @@
             reinvites = set()
             for attendee in self.calendar.getAllAttendeeProperties():
                 try:
-                    if attendee.parameterValue("SCHEDULE-FORCE-SEND") == "REQUEST":
+                    if attendee.parameterValue("SCHEDULE-FORCE-SEND", "").upper() == "REQUEST":
                         reinvites.add(attendee.value())
                 except KeyError:
                     pass
@@ -626,9 +630,7 @@
         
         # TODO: the later three will be ignored for now.
 
-        oldAttendeesByInstance = self.oldcalendar.getAttendeesByInstance(onlyScheduleAgentServer=True)
-        
-        mappedOld = set(oldAttendeesByInstance)
+        mappedOld = set(self.oldAttendeesByInstance)
         mappedNew = set(self.attendeesByInstance)
         
         # Get missing instances
@@ -684,6 +686,77 @@
                 if (attendee, rid) not in mappedNew and rid not in oldexdates:
                     self.cancelledAttendees.add((attendee, rid))
 
+    def coerceAttendeesPartstatOnCreate(self):
+        """
+        Make sure any attendees handled by the server start off with PARTSTAT=NEEDS-ACTION as
+        we do not allow the organizer to forcibly set PARTSTAT to anything else.
+        """
+        for attendee in self.calendar.getAllAttendeeProperties():
+            # Don't adjust ORGANIZER's ATTENDEE
+            if attendee.value() in self.organizerPrincipal.calendarUserAddresses():
+                continue
+            if attendee.parameterValue("SCHEDULE-AGENT", "SERVER").upper() == "SERVER" and attendee.hasParameter("PARTSTAT"):
+                attendee.setParameter("PARTSTAT", "NEEDS-ACTION")
+    
+    def coerceAttendeesPartstatOnModify(self):
+        """
+        Make sure that the organizer does not change attendees' PARTSTAT to anything
+        other than NEEDS-ACTION for those attendees handled by the server.
+        """
+        
+        # Get the set of Rids in each calendar
+        newRids = set(self.calendar.getComponentInstances())
+        oldRids = set(self.oldcalendar.getComponentInstances())
+        
+        # Test/fix ones that are the same
+        for rid in (newRids & oldRids):
+            self.compareAttendeePartstats(self.oldcalendar.overriddenComponent(rid), self.calendar.overriddenComponent(rid))
+        
+        # Test/fix ones added
+        for rid in (newRids - oldRids):
+            # Compare the new one to the old master
+            self.compareAttendeePartstats(self.oldcalendar.overriddenComponent(None), self.calendar.overriddenComponent(rid))
+
+        # For removals, we ignore ones that are no longer valid
+        valid_old_rids = self.calendar.validInstances(oldRids - newRids)
+    
+        # Test/fix ones removed         
+        for rid in valid_old_rids:
+            # Compare the old one to the new master
+            # Note it is hard to recover from this state so raise instead
+            self.compareAttendeePartstats(
+                self.oldcalendar.overriddenComponent(rid),
+                self.calendar.overriddenComponent(None),
+                raiseOnMisMatch=True
+            )
+        
+    def compareAttendeePartstats(self, old_component, new_component, raiseOnMisMatch=False):
+        """
+        Compare two components, old and new, and make sure the Organizer has not changed the PARTSTATs
+        in the new one to anything other than NEEDS-ACTION. If there is a change, undo it.
+        """
+        
+        old_attendees = dict([(normalizeCUAddr(attendee.value()), attendee) for attendee in old_component.getAllAttendeeProperties()])
+        new_attendees = dict([(normalizeCUAddr(attendee.value()), attendee) for attendee in new_component.getAllAttendeeProperties()])
+        
+        for cuaddr, newattendee in new_attendees.items():
+            # Don't adjust ORGANIZER's ATTENDEE
+            if newattendee.value() in self.organizerPrincipal.calendarUserAddresses():
+                continue
+            new_partstat = newattendee.parameterValue("PARTSTAT", "NEEDS-ACTION").upper()
+            if newattendee.parameterValue("SCHEDULE-AGENT", "SERVER").upper() == "SERVER" and new_partstat != "NEEDS-ACTION":
+                old_attendee = old_attendees.get(cuaddr)
+                old_partstat = old_attendee.parameterValue("PARTSTAT", "NEEDS-ACTION").upper() if old_attendee else "NEEDS-ACTION"
+                if old_attendee is None or old_partstat != new_partstat:
+                    if raiseOnMisMatch:
+                        raise HTTPError(ErrorResponse(
+                            responsecode.FORBIDDEN,
+                            (caldav_namespace, "valid-organizer-change"),
+                            "Organizer cannot change Attendee PARTSTAT",
+                        ))
+                    else:
+                        newattendee.setParameter("PARTSTAT", old_partstat)
+
     @inlineCallbacks
     def scheduleWithAttendees(self):
         
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20110815/dc2fadd9/attachment.html>


More information about the calendarserver-changes mailing list