[CalendarServer-changes] [14926] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Fri Jun 26 17:15:49 PDT 2015


Revision: 14926
          http://trac.calendarserver.org//changeset/14926
Author:   cdaboo at apple.com
Date:     2015-06-26 17:15:48 -0700 (Fri, 26 Jun 2015)
Log Message:
-----------
Make sure group reconciliation never attempts to share a calendar with the sharer.

Modified Paths:
--------------
    CalendarServer/trunk/twistedcaldav/ical.py
    CalendarServer/trunk/txdav/caldav/datastore/sql.py
    CalendarServer/trunk/txdav/who/test/accounts/groupAccounts.xml
    CalendarServer/trunk/txdav/who/test/test_group_sharees.py

Modified: CalendarServer/trunk/twistedcaldav/ical.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/ical.py	2015-06-26 21:21:39 UTC (rev 14925)
+++ CalendarServer/trunk/twistedcaldav/ical.py	2015-06-27 00:15:48 UTC (rev 14926)
@@ -3579,7 +3579,19 @@
 
 
     def _reconcileGroupAttendee(self, groupCUA, memberAttendeeProps):
+        """
+        Make sure there are attendee properties for every member of the group, and no
+        other attendee properties marked as a member of the group. Note that attendee
+        properties already present with a MEMBER parameter are not given a MEMBER
+        parameter if they are in the group. This ensures that manually added attendees
+        are not automatically removed when they dissappear from a group.
 
+        @param groupCUA: calendar user address of the group
+        @type groupCUA: L{str}
+        @param memberAttendeeProps: list of member properties
+        @type memberAttendeeProps: L{tuple}
+        """
+
         changed = False
         for component in self.subcomponents(ignore=True):
             oldAttendeeProps = tuple(component.properties("ATTENDEE"))
@@ -3615,7 +3627,15 @@
 
 
     def reconcileGroupAttendees(self, groupCUAToAttendeeMemberPropMap):
+        """
+        Reconcile the attendee properties in this L{Component}.
 
+        @param groupCUAToAttendeeMemberPropMap: map of group to potential attendees
+        @type groupCUAToAttendeeMemberPropMap: L{dict}
+        """
+
+        # Reconcile the member ship list of each group attendee, keeping track of which
+        # groups are actually used
         changed = False
         allMemberCUAs = set()
         nonemptyGroupCUAs = set()
@@ -3625,7 +3645,8 @@
             if memberAttendeeProps:
                 nonemptyGroupCUAs.add(groupCUA)
 
-        # remove orphans
+        # Remove attendee properties that have a MEMBER value that contains only groups no longer
+        # used in this component
         for component in self.subcomponents(ignore=True):
             for attendeeProp in tuple(component.properties("ATTENDEE")):
                 if attendeeProp.hasParameter("MEMBER"):

Modified: CalendarServer/trunk/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/sql.py	2015-06-26 21:21:39 UTC (rev 14925)
+++ CalendarServer/trunk/txdav/caldav/datastore/sql.py	2015-06-27 00:15:48 UTC (rev 14926)
@@ -2156,11 +2156,13 @@
                     changed = True
 
             for memberUID in memberUIDs - boundUIDs:
-                shareeView = yield self.shareeView(memberUID)
-                newMode = _BIND_MODE_GROUP if shareeView is None else shareeView._groupModeAfterAddingOneGroupSharee()
-                if newMode is not None:
-                    yield super(Calendar, self).inviteUIDToShare(memberUID, newMode)
-                    changed = True
+                # Never reconcile the sharer
+                if memberUID != self._home.uid():
+                    shareeView = yield self.shareeView(memberUID)
+                    newMode = _BIND_MODE_GROUP if shareeView is None else shareeView._groupModeAfterAddingOneGroupSharee()
+                    if newMode is not None:
+                        yield super(Calendar, self).inviteUIDToShare(memberUID, newMode)
+                        changed = True
 
         returnValue(changed)
 
@@ -2744,6 +2746,7 @@
             if attendeeProp.parameterValue("CUTYPE") == "X-SERVER-GROUP"
         ])
 
+        # Map each group attendee to a list of potential member properties
         groupCUAToAttendeeMemberPropMap = {}
         for groupCUA in groupCUAs:
 

Modified: CalendarServer/trunk/txdav/who/test/accounts/groupAccounts.xml
===================================================================
--- CalendarServer/trunk/txdav/who/test/accounts/groupAccounts.xml	2015-06-26 21:21:39 UTC (rev 14925)
+++ CalendarServer/trunk/txdav/who/test/accounts/groupAccounts.xml	2015-06-27 00:15:48 UTC (rev 14926)
@@ -136,4 +136,21 @@
 	    <member-uid>group03</member-uid>
 	    <member-uid>user10</member-uid>
 	</record>
+	<record type="group">
+	    <short-name>group05</short-name>
+	    <uid>group05</uid>
+	    <guid>20000000-0000-0000-0000-000000000005</guid>
+	    <full-name>Group 05</full-name>
+	    <email>group05 at example.com</email>
+	    <member-uid>user01</member-uid>
+	    <member-uid>user02</member-uid>
+	</record>
+	<record type="group">
+	    <short-name>group06</short-name>
+	    <uid>group06</uid>
+	    <guid>20000000-0000-0000-0000-000000000006</guid>
+	    <full-name>Group 06</full-name>
+	    <email>group06 at example.com</email>
+	    <member-uid>user02</member-uid>
+	</record>
 </directory>

Modified: CalendarServer/trunk/txdav/who/test/test_group_sharees.py
===================================================================
--- CalendarServer/trunk/txdav/who/test/test_group_sharees.py	2015-06-26 21:21:39 UTC (rev 14925)
+++ CalendarServer/trunk/txdav/who/test/test_group_sharees.py	2015-06-27 00:15:48 UTC (rev 14926)
@@ -348,3 +348,164 @@
         yield calendar.uninviteUIDFromShare("group02")
         noinvites = yield calendar.sharingInvites()
         self.assertEqual(len(noinvites), 3)
+
+
+    @inlineCallbacks
+    def test_no_self_invite(self):
+        """
+        Test that group shares where the group includes the sharee work. Then remove
+        the sharee from the group and make sure it works.
+        """
+
+        record02 = yield self.transactionUnderTest().directoryService().recordWithUID("user02")
+
+        @inlineCallbacks
+        def expandedMembers(self, records=None, seen=None):
+
+            if self.uid == "group05":
+                returnValue(frozenset((record02,)))
+            else:
+                returnValue((yield unpatchedExpandedMembers(self, records, seen)))
+
+        unpatchedExpandedMembers = CalendarDirectoryRecordMixin.expandedMembers
+
+        # setup group cacher
+        groupCacher = GroupCacher(self.transactionUnderTest().directoryService())
+        groupsToRefresh = yield groupCacher.groupsToRefresh(self.transactionUnderTest())
+        self.assertEqual(len(groupsToRefresh), 0)
+        wps = yield groupCacher.refreshGroup(self.transactionUnderTest(), "group05")
+        self.assertEqual(len(wps), 0)
+
+        yield self._check_notifications("user01", [])
+
+        # Invite
+        calendar = yield self.calendarUnderTest(home="user01", name="calendar")
+        invites = yield calendar.sharingInvites()
+        self.assertEqual(len(invites), 0)
+        self.assertFalse(calendar.isShared())
+
+        yield self._check_notifications("user01", [])
+        shareeViews = yield calendar.inviteUIDToShare("group05", _BIND_MODE_READ)
+        self.assertEqual(len(shareeViews), 1)
+        calendar = yield self.calendarUnderTest(home="user01", name="calendar")
+        invites = yield calendar.sharingInvites()
+        self.assertEqual(len(invites), 1)
+        for invite in invites:
+            shareeView = yield calendar.shareeView(invite.shareeUID)
+            self.assertEqual(invite.ownerUID, "user01")
+            self.assertEqual(invite.uid, shareeView.shareName())
+            self.assertEqual(invite.mode, _BIND_MODE_GROUP)
+            self.assertEqual((yield shareeView.effectiveShareMode()), _BIND_MODE_READ)
+            self.assertEqual(invite.status, _BIND_STATUS_INVITED)
+            self.assertEqual(invite.summary, None)
+            yield self._check_notifications(invite.shareeUID, [invite.uid, ])
+
+        # 1 group members
+        self.patch(CalendarDirectoryRecordMixin, "expandedMembers", expandedMembers)
+
+        wps = yield groupCacher.refreshGroup(self.transactionUnderTest(), "group05")
+        self.assertEqual(len(wps), 1)
+        yield self.commit()
+        yield JobItem.waitEmpty(self._sqlCalendarStore.newTransaction, reactor, 60)
+
+        calendar = yield self.calendarUnderTest(home="user01", name="calendar")
+        invites = yield calendar.sharingInvites()
+        self.assertEqual(len(invites), 1)
+        for invite in invites:
+            shareeView = yield calendar.shareeView(invite.shareeUID)
+            self.assertEqual(invite.ownerUID, "user01")
+            self.assertEqual(invite.uid, shareeView.shareName())
+            self.assertEqual(invite.mode, _BIND_MODE_GROUP)
+            self.assertEqual((yield shareeView.effectiveShareMode()), _BIND_MODE_READ)
+            self.assertEqual(invite.status, _BIND_STATUS_INVITED)
+            self.assertEqual(invite.summary, None)
+            yield self._check_notifications(invite.shareeUID, [invite.uid, ])
+
+        yield self._check_notifications("user01", [])
+
+        # Uninvite
+        calendar = yield self.calendarUnderTest(home="user01", name="calendar")
+        yield calendar.uninviteUIDFromShare("group05")
+        noinvites = yield calendar.sharingInvites()
+        self.assertEqual(len(noinvites), 0)
+
+
+    @inlineCallbacks
+    def test_no_self_invite_on_add(self):
+        """
+        Test that the sharee is not invited to their own share when they are added as a member
+        of a group to whom the calendar is shared.
+        """
+
+        record01 = yield self.transactionUnderTest().directoryService().recordWithUID("user01")
+        record02 = yield self.transactionUnderTest().directoryService().recordWithUID("user02")
+
+        @inlineCallbacks
+        def expandedMembers(self, records=None, seen=None):
+
+            if self.uid == "group06":
+                returnValue(frozenset((record01, record02,)))
+            else:
+                returnValue((yield unpatchedExpandedMembers(self, records, seen)))
+
+        unpatchedExpandedMembers = CalendarDirectoryRecordMixin.expandedMembers
+
+        # setup group cacher
+        groupCacher = GroupCacher(self.transactionUnderTest().directoryService())
+        groupsToRefresh = yield groupCacher.groupsToRefresh(self.transactionUnderTest())
+        self.assertEqual(len(groupsToRefresh), 0)
+        wps = yield groupCacher.refreshGroup(self.transactionUnderTest(), "group06")
+        self.assertEqual(len(wps), 0)
+
+        yield self._check_notifications("user01", [])
+
+        # Invite
+        calendar = yield self.calendarUnderTest(home="user01", name="calendar")
+        invites = yield calendar.sharingInvites()
+        self.assertEqual(len(invites), 0)
+        self.assertFalse(calendar.isShared())
+
+        yield self._check_notifications("user01", [])
+        shareeViews = yield calendar.inviteUIDToShare("group06", _BIND_MODE_READ)
+        self.assertEqual(len(shareeViews), 1)
+        calendar = yield self.calendarUnderTest(home="user01", name="calendar")
+        invites = yield calendar.sharingInvites()
+        self.assertEqual(len(invites), 1)
+        for invite in invites:
+            shareeView = yield calendar.shareeView(invite.shareeUID)
+            self.assertEqual(invite.ownerUID, "user01")
+            self.assertEqual(invite.uid, shareeView.shareName())
+            self.assertEqual(invite.mode, _BIND_MODE_GROUP)
+            self.assertEqual((yield shareeView.effectiveShareMode()), _BIND_MODE_READ)
+            self.assertEqual(invite.status, _BIND_STATUS_INVITED)
+            self.assertEqual(invite.summary, None)
+            yield self._check_notifications(invite.shareeUID, [invite.uid, ])
+
+        # 1 group members
+        self.patch(CalendarDirectoryRecordMixin, "expandedMembers", expandedMembers)
+
+        wps = yield groupCacher.refreshGroup(self.transactionUnderTest(), "group06")
+        self.assertEqual(len(wps), 1)
+        yield self.commit()
+        yield JobItem.waitEmpty(self._sqlCalendarStore.newTransaction, reactor, 60)
+
+        calendar = yield self.calendarUnderTest(home="user01", name="calendar")
+        invites = yield calendar.sharingInvites()
+        self.assertEqual(len(invites), 1)
+        for invite in invites:
+            shareeView = yield calendar.shareeView(invite.shareeUID)
+            self.assertEqual(invite.ownerUID, "user01")
+            self.assertEqual(invite.uid, shareeView.shareName())
+            self.assertEqual(invite.mode, _BIND_MODE_GROUP)
+            self.assertEqual((yield shareeView.effectiveShareMode()), _BIND_MODE_READ)
+            self.assertEqual(invite.status, _BIND_STATUS_INVITED)
+            self.assertEqual(invite.summary, None)
+            yield self._check_notifications(invite.shareeUID, [invite.uid, ])
+
+        yield self._check_notifications("user01", [])
+
+        # Uninvite
+        calendar = yield self.calendarUnderTest(home="user01", name="calendar")
+        yield calendar.uninviteUIDFromShare("group06")
+        noinvites = yield calendar.sharingInvites()
+        self.assertEqual(len(noinvites), 0)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20150626/11417e38/attachment-0001.html>


More information about the calendarserver-changes mailing list