Revision: 3601 http://trac.macosforge.org/projects/calendarserver/changeset/3601 Author: cdaboo@apple.com Date: 2009-01-20 13:02:18 -0800 (Tue, 20 Jan 2009) Log Message: ----------- Make sure entirely cancelled event written by ATTENDEE is ignored rather than generating a 403. Modified Paths: -------------- CalendarServer/trunk/run CalendarServer/trunk/twistedcaldav/ical.py CalendarServer/trunk/twistedcaldav/scheduling/icaldiff.py CalendarServer/trunk/twistedcaldav/scheduling/implicit.py CalendarServer/trunk/twistedcaldav/scheduling/test/test_icaldiff.py Modified: CalendarServer/trunk/run =================================================================== --- CalendarServer/trunk/run 2009-01-20 21:01:01 UTC (rev 3600) +++ CalendarServer/trunk/run 2009-01-20 21:02:18 UTC (rev 3601) @@ -692,7 +692,7 @@ caldavtester="${top}/CalDAVTester"; -svn_get "CalDAVTester" "${caldavtester}" "${svn_uri_base}/CalDAVTester/trunk" 3587; +svn_get "CalDAVTester" "${caldavtester}" "${svn_uri_base}/CalDAVTester/trunk" 3600; # # Calendar Server Modified: CalendarServer/trunk/twistedcaldav/ical.py =================================================================== --- CalendarServer/trunk/twistedcaldav/ical.py 2009-01-20 21:01:01 UTC (rev 3600) +++ CalendarServer/trunk/twistedcaldav/ical.py 2009-01-20 21:02:18 UTC (rev 3601) @@ -1724,6 +1724,14 @@ self.removeProperty(duration) self.addProperty(Property("DTEND", newdtend)) + exdates = self.properties("EXDATE") + for exdate in exdates: + exdate.setValue([normalizeToUTC(value) for value in exdate.value()]) + try: + del exdate.params()["TZID"] + except KeyError: + pass + rid = self.getProperty("RECURRENCE-ID") if rid is not None: rid.setValue(normalizeToUTC(rid.value())) Modified: CalendarServer/trunk/twistedcaldav/scheduling/icaldiff.py =================================================================== --- CalendarServer/trunk/twistedcaldav/scheduling/icaldiff.py 2009-01-20 21:01:01 UTC (rev 3600) +++ CalendarServer/trunk/twistedcaldav/scheduling/icaldiff.py 2009-01-20 21:02:18 UTC (rev 3601) @@ -14,7 +14,7 @@ # limitations under the License. ## -from twistedcaldav.ical import Component +from twistedcaldav.ical import Component, Property from twistedcaldav.log import Logger from twistedcaldav.scheduling.cuaddress import normalizeCUAddr from twistedcaldav.scheduling.itip import iTipGenerator @@ -239,19 +239,19 @@ self.calendar2 = duplicateAndNormalize(self.calendar2) if self.calendar1 == self.calendar2: - return True, True + return True, True, () # Need to look at each component and do special comparisons # Make sure the same VCALENDAR properties match if not self._checkVCALENDARProperties(): self._logDiffError("attendeeDiff: VCALENDAR properties do not match") - return False, False + return False, False, () # Make sure the same VTIMEZONE components appear if not self._compareVTIMEZONEs(): self._logDiffError("attendeeDiff: VTIMEZONEs do not match") - return False, False + return False, False, () # Compare each component instance from the new calendar with each derived # component instance from the old one @@ -377,37 +377,56 @@ map2 = mapComponents(self.calendar2) set2 = set(map2.keys()) + # Ugly case: if an Attendee has a STATUS:CANCELLED meeting and the ORGANIZER does not, + # we may need to remove an EXDATE for the cancelled instance from the ORGANIZER's + # master instance to ensure that matches + cancelled_rids = [] + master2 = self.calendar2.masterComponent() + for key in set2 - set1: + component2 = map2[key] + if component2.propertyValue("STATUS") == "CANCELLED": + rid = component2.getRecurrenceIDUTC() + cancelled_rids.append(rid) + if master2: + master2.addProperty(Property("EXDATE", [rid,])) + # All the components in calendar1 must be in calendar2 result = set1 - set2 if result: log.debug("Missing components from first calendar: %s" % (result,)) - return False, False + return False, False, () # Now verify that each component in set1 matches what is in set2 attendee_unchanged = True for key, value in map1.iteritems(): component1 = value component2 = map2[key] - + nomismatch, no_attendee_change = self._testComponents(component1, component2) if not nomismatch: - return False, False + return False, False, () attendee_unchanged &= no_attendee_change # Now verify that each additional component in set2 matches a derived component in set1 for key in set2 - set1: + + # First check if the attendee's copy is cancelled + component2 = map2[key] + if component2.propertyValue("STATUS") == "CANCELLED": + continue + + # Now derive the organizer's expected instance and compare component1 = self.calendar1.deriveInstance(key[2]) if component1 is None: log.debug("_compareComponents: Could not derive instance: %s" % (key[2],)) - return False, False - component2 = map2[key] + return False, False, () nomismatch, no_attendee_change = self._testComponents(component1, component2) if not nomismatch: - return False, False + return False, False, () attendee_unchanged &= no_attendee_change - return True, attendee_unchanged + return True, attendee_unchanged, tuple(cancelled_rids) def _testComponents(self, comp1, comp2): Modified: CalendarServer/trunk/twistedcaldav/scheduling/implicit.py =================================================================== --- CalendarServer/trunk/twistedcaldav/scheduling/implicit.py 2009-01-20 21:01:01 UTC (rev 3600) +++ CalendarServer/trunk/twistedcaldav/scheduling/implicit.py 2009-01-20 21:02:18 UTC (rev 3601) @@ -662,13 +662,39 @@ self.oldcalendar = None # Determine whether the current change is allowed - if self.isAttendeeChangeInsignificant(): + change_allowed, no_itip, cancelled_rids = self.isAttendeeChangeInsignificant() + + if not change_allowed: + if self.calendar.hasPropertyValueInAllComponents(Property("STATUS", "CANCELLED")): + log.debug("Attendee '%s' is creating CANCELLED event for mismatched UID: '%s' - removing entire event" % (self.attendee, self.uid,)) + self.return_status = ImplicitScheduler.STATUS_ORPHANED_CANCELLED_EVENT + returnValue(None) + else: + log.error("Attendee '%s' is not allowed to make an unauthorized change to an organized event: UID:%s" % (self.attendeePrincipal, self.uid,)) + raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-attendee-change"))) + + # Remove orphaned attendee cancelled events + if cancelled_rids: + log.debug("Attendee '%s' is creating CANCELLED overridden instances for UID: '%s' - removing instances" % (self.attendee, self.uid,)) + master = self.calendar.masterComponent() + for rid in cancelled_rids: + self.calendar.removeComponent(self.calendar.overriddenComponent(rid)) + if master: + master.addProperty(Property("EXDATE", [rid,])) + + # If no components left, make sure we delete the orphaned event + if self.calendar.mainType() is None: + log.debug("Attendee '%s' CANCELLED all instances of UID: '%s' - removing entire event" % (self.attendee, self.uid,)) + self.return_status = ImplicitScheduler.STATUS_ORPHANED_CANCELLED_EVENT + returnValue(None) + + if no_itip: log.debug("Implicit - attendee '%s' is updating UID: '%s' but change is not significant" % (self.attendee, self.uid)) returnValue(None) elif isinstance(self.organizerAddress, LocalCalendarUser): # Check to see whether all instances are CANCELLED if self.calendar.hasPropertyValueInAllComponents(Property("STATUS", "CANCELLED")): - log.debug("Attendee '%s' is creating CANCELLED event for UID: '%s' - missing organizer copy" % (self.attendee, self.uid,)) + log.debug("Attendee '%s' is creating CANCELLED event for missing UID: '%s' - removing entire event" % (self.attendee, self.uid,)) self.return_status = ImplicitScheduler.STATUS_ORPHANED_CANCELLED_EVENT returnValue(None) else: @@ -732,12 +758,9 @@ oldcalendar = self.organizer_calendar oldcalendar.attendeesView((self.attendee,)) differ = iCalDiff(oldcalendar, self.calendar, self.do_smart_merge) - change_allowed, no_itip = differ.attendeeDiff(self.attendee) - if not change_allowed: - log.error("Attendee '%s' is not allowed to make an unauthorized change to an organized event: UID:%s" % (self.attendeePrincipal, self.uid,)) - raise HTTPError(ErrorResponse(responsecode.FORBIDDEN, (caldav_namespace, "valid-attendee-change"))) + change_allowed, no_itip, cancelled_rids = differ.attendeeDiff(self.attendee) - return no_itip + return change_allowed, no_itip, cancelled_rids def scheduleWithOrganizer(self): Modified: CalendarServer/trunk/twistedcaldav/scheduling/test/test_icaldiff.py =================================================================== --- CalendarServer/trunk/twistedcaldav/scheduling/test/test_icaldiff.py 2009-01-20 21:01:01 UTC (rev 3600) +++ CalendarServer/trunk/twistedcaldav/scheduling/test/test_icaldiff.py 2009-01-20 21:02:18 UTC (rev 3601) @@ -506,7 +506,7 @@ END:VCALENDAR """, "mailto:user2@example.com", - (True, True,) + (True, True, (),) ), ( "#1.2 Simple component, PARTSTAT change", @@ -537,7 +537,7 @@ END:VCALENDAR """, "mailto:user2@example.com", - (True, False,) + (True, False, (),) ), ( "#1.3 Simple component, bad change", @@ -568,7 +568,7 @@ END:VCALENDAR """, "mailto:user2@example.com", - (False, False,) + (False, False, (),) ), ( "#1.4 Simple component, valarm change", @@ -609,7 +609,7 @@ END:VCALENDAR """, "mailto:user2@example.com", - (True, True,) + (True, True, (),) ), ( "#1.5 Simple component, vcalendar props change ok", @@ -651,7 +651,7 @@ END:VCALENDAR """, "mailto:user2@example.com", - (True, True,) + (True, True, (),) ), ( "#1.6 Simple component, vcalendar props change bad", @@ -693,7 +693,7 @@ END:VCALENDAR """, "mailto:user2@example.com", - (True, True,) + (True, True, (),) ), ( "#1.7 Simple component, vtimezone no change", @@ -760,7 +760,7 @@ END:VCALENDAR """, "mailto:user2@example.com", - (True, True,) + (True, True, (),) ), ( "#1.8 Simple component, vtimezone bad change", @@ -827,7 +827,7 @@ END:VCALENDAR """, "mailto:user2@example.com", - (False, False,) + (False, False, (),) ), ) @@ -885,7 +885,7 @@ END:VCALENDAR """, "mailto:user2@example.com", - (True, True,) + (True, True, (),) ), ( "#1.2 Complex component, alarm change", @@ -944,7 +944,7 @@ END:VCALENDAR """, "mailto:user2@example.com", - (True, True,) + (True, True, (),) ), ( "#1.3 Complex component, missing override", @@ -985,7 +985,7 @@ END:VCALENDAR """, "mailto:user2@example.com", - (False, False,) + (False, False, (),) ), ( "#1.4 Complex component, additional override no change ok", @@ -1043,7 +1043,7 @@ END:VCALENDAR """, "mailto:user2@example.com", - (True, True,) + (True, True, (),) ), ( "#1.5 Complex component, additional override change ok", @@ -1101,7 +1101,7 @@ END:VCALENDAR """, "mailto:user2@example.com", - (True, False,) + (True, False, (),) ), ( "#1.6 Complex component, additional override bad", @@ -1159,7 +1159,7 @@ END:VCALENDAR """, "mailto:user2@example.com", - (False, False,) + (False, False, (),) ), )
participants (1)
-
source_changes@macosforge.org