class AugmentTest(StoreTestCase): @inlineCallbacks def setUp(self): yield super(AugmentTest, self).setUp() self.groupCacher = GroupCacher(self.directory) @inlineCallbacks def test_groups(self): """ Make sure augmented record groups( ) returns only the groups that have been refreshed. """ store = self.storeUnderTest() txn = store.newTransaction() yield self.groupCacher.refreshGroup(txn, u"__top_group_1__") yield txn.commit() record = yield self.directory.recordWithUID(u"__sagen1__") groups = yield record.groups() self.assertEquals(set(["__top_group_1__"]), set([g.uid for g in groups])) txn = store.newTransaction() yield self.groupCacher.refreshGroup(txn, u"__sub_group_1__") yield txn.commit() record = yield self.directory.recordWithUID(u"__sagen1__") groups = yield record.groups() self.assertEquals(set(["__top_group_1__", "__sub_group_1__"]), set([g.uid for g in groups]))
def test_groups(self): # A group must first be "refreshed" into the DB otherwise we won't # consider it for group memberships txn = self.store.newTransaction() groupCacher = GroupCacher(self.directory) yield groupCacher.refreshGroup(txn, u"__sub_group_1__") yield txn.commit()
def setUp(self): yield super(DynamicGroupTest, self).setUp() self.directory = CalendarInMemoryDirectoryService(None) self.store.setDirectoryService(self.directory) self.groupCacher = GroupCacher(self.directory) self.numUsers = 100 # Add users records = [] fieldName = self.directory.fieldName for i in xrange(self.numUsers): records.append( TestRecord( self.directory, { fieldName.uid: u"foo{ctr:05d}".format(ctr=i), fieldName.shortNames: (u"foo{ctr:05d}".format(ctr=i),), fieldName.fullNames: (u"foo{ctr:05d}".format(ctr=i),), fieldName.recordType: RecordType.user, } ) ) # Add two groups for uid in (u"testgroup", u"emptygroup",): records.append( TestRecord( self.directory, { fieldName.uid: uid, fieldName.recordType: RecordType.group, } ) ) yield self.directory.updateRecords(records, create=True) # add members to test group group = yield self.directory.recordWithUID(u"testgroup") members = yield self.directory.recordsWithRecordType(RecordType.user) yield group.setMembers(members) def doWork(self): self.transaction._groupCacher = groupCacher return unpatchedDoWork(self) groupCacher = self.groupCacher unpatchedDoWork = GroupRefreshWork.doWork self.patch(GroupRefreshWork, "doWork", doWork) config.AutomaticPurging.Enabled = True
def setUp(self): yield super(DelegationCachingTest, self).setUp() self.store = self.storeUnderTest() self.groupCacher = GroupCacher(self.directory) yield Delegates._memcacher.flushAll() self.patch(CachingDelegates, "cacheNotifier", CapturingCacheNotifier())
class AugmentTest(StoreTestCase): @inlineCallbacks def setUp(self): yield super(AugmentTest, self).setUp() self.groupCacher = GroupCacher(self.directory) @inlineCallbacks def test_groups(self): """ Make sure augmented record groups( ) returns only the groups that have been refreshed. """ store = self.storeUnderTest() txn = store.newTransaction() yield self.groupCacher.refreshGroup(txn, u"__top_group_1__") yield txn.commit() record = yield self.directory.recordWithUID(u"__sagen1__") groups = yield record.groups() self.assertEquals( set(["__top_group_1__"]), set([g.uid for g in groups]) ) txn = store.newTransaction() yield self.groupCacher.refreshGroup(txn, u"__sub_group_1__") yield txn.commit() record = yield self.directory.recordWithUID(u"__sagen1__") groups = yield record.groups() self.assertEquals( set(["__top_group_1__", "__sub_group_1__"]), set([g.uid for g in groups]) )
def setUp(self): yield super(DynamicGroupTest, self).setUp() self.directory = CalendarInMemoryDirectoryService(None) self.store.setDirectoryService(self.directory) self.groupCacher = GroupCacher(self.directory) self.numUsers = 100 # Add users records = [] fieldName = self.directory.fieldName for i in xrange(self.numUsers): records.append( TestRecord( self.directory, { fieldName.uid: u"foo{ctr:05d}".format(ctr=i), fieldName.shortNames: (u"foo{ctr:05d}".format(ctr=i), ), fieldName.fullNames: (u"foo{ctr:05d}".format(ctr=i), ), fieldName.recordType: RecordType.user, })) # Add a group records.append( TestRecord( self.directory, { fieldName.uid: u"testgroup", fieldName.recordType: RecordType.group, })) yield self.directory.updateRecords(records, create=True) group = yield self.directory.recordWithUID(u"testgroup") members = yield self.directory.recordsWithRecordType(RecordType.user) yield group.setMembers(members)
def setUp(self): yield super(DynamicGroupTest, self).setUp() self.directory = CalendarInMemoryDirectoryService(None) self.store.setDirectoryService(self.directory) self.groupCacher = GroupCacher(self.directory) self.numUsers = 100 # Add users records = [] fieldName = self.directory.fieldName for i in xrange(self.numUsers): records.append( TestRecord( self.directory, { fieldName.uid: u"foo{ctr:05d}".format(ctr=i), fieldName.shortNames: (u"foo{ctr:05d}".format(ctr=i),), fieldName.fullNames: (u"foo{ctr:05d}".format(ctr=i),), fieldName.recordType: RecordType.user, } ) ) # Add a group records.append( TestRecord( self.directory, { fieldName.uid: u"testgroup", fieldName.recordType: RecordType.group, } ) ) yield self.directory.updateRecords(records, create=True) group = yield self.directory.recordWithUID(u"testgroup") members = yield self.directory.recordsWithRecordType(RecordType.user) yield group.setMembers(members)
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.isSharedByOwner()) 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)
def test_group_change_removed_calendar(self): """ Test that group shares are properly cleaned when a calendar is removed (and not trashed). """ self.patch(config, "EnableTrashCollection", False) @inlineCallbacks def expandedMembers(self, records=None, seen=None): if self.uid == "group02": returnValue(frozenset()) 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(), "group02") self.assertEqual(len(wps), 0) yield self._check_notifications("user01", []) # New calendar for sharing home = yield self.homeUnderTest(name="user01") yield home.createCalendarWithName("shared") yield self.commit() # Invite calendar = yield self.calendarUnderTest(home="user01", name="shared") invites = yield calendar.sharingInvites() self.assertEqual(len(invites), 0) self.assertFalse(calendar.isSharedByOwner()) yield self._check_notifications("user01", []) shareeViews = yield calendar.inviteUIDToShare("group02", _BIND_MODE_READ) self.assertEqual(len(shareeViews), 3) calendar = yield self.calendarUnderTest(home="user01", name="shared") invites = yield calendar.sharingInvites() self.assertEqual(len(invites), 3) 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, ]) groupsToRefresh = yield groupCacher.groupsToRefresh( self.transactionUnderTest()) self.assertEqual(len(groupsToRefresh), 1) yield self.commit() # Remove the collection calendar = yield self.calendarUnderTest(home="user01", name="shared") remove_id = calendar.id() yield calendar.remove() yield self.commit() home = yield self.homeUnderTest(name="user01") calendar = yield home.childWithID(remove_id) self.assertTrue(calendar is None) calendar = yield home.childWithID(remove_id, onlyInTrash=True) self.assertTrue(calendar is None) yield self.commit() # 1 group member self.patch(CalendarDirectoryRecordMixin, "expandedMembers", expandedMembers) wps = yield groupCacher.refreshGroup(self.transactionUnderTest(), "group02") self.assertEqual(len(wps), 0) yield self.commit() yield JobItem.waitEmpty(self._sqlCalendarStore.newTransaction, reactor, 60)
def test_group_change_invite_larger(self): """ Test that group shares are changed when the group changes. """ @inlineCallbacks def expandedMembers(self, records=None, seen=None): if self.uid == "group02" or self.uid == "group03": returnValue(frozenset()) else: returnValue((yield unpatchedExpandedMembers(self, records, seen))) unpatchedExpandedMembers = CalendarDirectoryRecordMixin.expandedMembers # 1 group member self.patch(CalendarDirectoryRecordMixin, "expandedMembers", 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(), "group04") 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.isSharedByOwner()) yield self._check_notifications("user01", []) shareeViews = yield calendar.inviteUIDToShare("group04", _BIND_MODE_READ) self.assertEqual(len(shareeViews), 1) invites = yield calendar.sharingInvites() self.assertEqual(len(invites), 1) for invite in invites: self.assertEqual(invite.shareeUID, "user10") 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, ]) groupsToRefresh = yield groupCacher.groupsToRefresh( self.transactionUnderTest()) self.assertEqual(len(groupsToRefresh), 1) # group members restored self.patch(CalendarDirectoryRecordMixin, "expandedMembers", unpatchedExpandedMembers) wps = yield groupCacher.refreshGroup(self.transactionUnderTest(), "group04") 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), 5) 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, ]) # Uninvite calendar = yield self.calendarUnderTest(home="user01", name="calendar") yield calendar.uninviteUIDFromShare("group04") noinvites = yield calendar.sharingInvites() self.assertEqual(len(noinvites), 0)
def test_group_member_removal_refresh_slow(self): """ Test that the sharee list is still valid when a member is removed from a group, but sharee reconciliation has not yet occurred. """ @inlineCallbacks def expandedMembers(self, records=None, seen=None): if self.uid == "group02": returnValue(frozenset()) else: returnValue((yield unpatchedExpandedMembers(self, records, seen))) unpatchedExpandedMembers = CalendarDirectoryRecordMixin.expandedMembers # Prevent sharee reconciliation def _noop(self): return succeed(None) self.patch(GroupShareeReconciliationWork, "doWork", _noop) # 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(), "group02") 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.isSharedByOwner()) yield self._check_notifications("user01", []) shareeViews = yield calendar.inviteUIDToShare("group02", _BIND_MODE_READ) self.assertEqual(len(shareeViews), 3) calendar = yield self.calendarUnderTest(home="user01", name="calendar") invites = yield calendar.sharingInvites() self.assertEqual(len(invites), 3) 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, ]) groupsToRefresh = yield groupCacher.groupsToRefresh( self.transactionUnderTest()) self.assertEqual(len(groupsToRefresh), 1) # 0 group members self.patch(CalendarDirectoryRecordMixin, "expandedMembers", expandedMembers) wps = yield groupCacher.refreshGroup(self.transactionUnderTest(), "group02") 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), 3) 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("group02") noinvites = yield calendar.sharingInvites() self.assertEqual(len(noinvites), 3)
class DynamicGroupTest(StoreTestCase): @inlineCallbacks def setUp(self): yield super(DynamicGroupTest, self).setUp() self.directory = CalendarInMemoryDirectoryService(None) self.store.setDirectoryService(self.directory) self.groupCacher = GroupCacher(self.directory) self.numUsers = 100 # Add users records = [] fieldName = self.directory.fieldName for i in xrange(self.numUsers): records.append( TestRecord( self.directory, { fieldName.uid: u"foo{ctr:05d}".format(ctr=i), fieldName.shortNames: (u"foo{ctr:05d}".format(ctr=i),), fieldName.fullNames: (u"foo{ctr:05d}".format(ctr=i),), fieldName.recordType: RecordType.user, } ) ) # Add two groups for uid in (u"testgroup", u"emptygroup",): records.append( TestRecord( self.directory, { fieldName.uid: uid, fieldName.recordType: RecordType.group, } ) ) yield self.directory.updateRecords(records, create=True) # add members to test group group = yield self.directory.recordWithUID(u"testgroup") members = yield self.directory.recordsWithRecordType(RecordType.user) yield group.setMembers(members) def doWork(self): self.transaction._groupCacher = groupCacher return unpatchedDoWork(self) groupCacher = self.groupCacher unpatchedDoWork = GroupRefreshWork.doWork self.patch(GroupRefreshWork, "doWork", doWork) config.AutomaticPurging.Enabled = True @inlineCallbacks def test_extant(self): """ Verify that once a group is removed from the directory, the next call to refreshGroup() will set the "extent" to False. Add the group back to the directory and "extent" becomes True. """ store = self.storeUnderTest() for uid in (u"testgroup", u"emptygroup",): txn = store.newTransaction() yield self.groupCacher.refreshGroup(txn, uid) group = yield txn.groupByUID(uid) yield txn.commit() self.assertTrue(group.extant) # Remove the group yield self.directory.removeRecords([uid]) txn = store.newTransaction() yield self.groupCacher.refreshGroup(txn, uid) group = (yield txn.groupByUID(uid)) yield txn.commit() # Extant = False self.assertFalse(group.extant) # The list of members stored in the DB for this group is now empty txn = store.newTransaction() members = yield txn.groupMemberUIDs(group.groupID) yield txn.commit() self.assertEquals(members, set()) # Add the group back into the directory fieldName = self.directory.fieldName yield self.directory.updateRecords( ( TestRecord( self.directory, { fieldName.uid: uid, fieldName.recordType: RecordType.group, } ), ), create=True ) if uid == u"testgroup": group = yield self.directory.recordWithUID(uid) members = yield self.directory.recordsWithRecordType(RecordType.user) yield group.setMembers(members) txn = store.newTransaction() yield self.groupCacher.refreshGroup(txn, uid) group = (yield txn.groupByUID(uid)) yield txn.commit() # Extant = True self.assertTrue(group.extant) # The list of members stored in the DB for this group has 100 users txn = store.newTransaction() members = yield txn.groupMemberUIDs(group.groupID) yield txn.commit() self.assertEquals(len(members), 100 if uid == u"testgroup" else 0) @inlineCallbacks def test_update_delete_unused(self): """ Verify that unused groups are deleted from group cache """ store = self.storeUnderTest() # unused group deleted for uid in (u"testgroup", u"emptygroup",): txn = store.newTransaction() yield self.groupCacher.refreshGroup(txn, uid) group = yield txn.groupByUID(uid, create=False) yield txn.commit() self.assertNotEqual(group, None) txn = store.newTransaction() yield self.groupCacher.update(txn) group = yield txn.groupByUID(uid, create=False) yield txn.commit() self.assertEqual(group, None) # delegate groups not deleted for uid in (u"testgroup", u"emptygroup",): txn = store.newTransaction() group = yield txn.groupByUID(uid) yield txn.addDelegateGroup(delegator=u"sagen", delegateGroupID=group.groupID, readWrite=True) yield txn.commit() self.assertNotEqual(group, None) txn = store.newTransaction() yield self.groupCacher.update(txn) yield txn.commit() yield JobItem.waitEmpty(store.newTransaction, reactor, 60) txn = store.newTransaction() group = yield txn.groupByUID(uid, create=False) yield txn.commit() self.assertNotEqual(group, None) # delegate group is deleted. unused group is deleted txn = store.newTransaction() testGroup = yield txn.groupByUID(u"testgroup", create=False) yield txn.removeDelegateGroup(delegator=u"sagen", delegateGroupID=testGroup.groupID, readWrite=True) testGroup = yield txn.groupByUID(u"testgroup", create=False) emptyGroup = yield txn.groupByUID(u"emptygroup", create=False) yield txn.commit() self.assertNotEqual(testGroup, None) self.assertNotEqual(emptyGroup, None) txn = store.newTransaction() yield self.groupCacher.update(txn) yield txn.commit() yield JobItem.waitEmpty(store.newTransaction, reactor, 60) txn = store.newTransaction() testGroup = yield txn.groupByUID(u"testgroup", create=False) emptyGroup = yield txn.groupByUID(u"emptygroup", create=False) yield txn.commit() self.assertEqual(testGroup, None) self.assertNotEqual(emptyGroup, None) @inlineCallbacks def test_update_delete_old_nonextant(self): """ Verify that old missing groups are deleted from group cache """ oldGroupPurgeIntervalSeconds = config.AutomaticPurging.GroupPurgeIntervalSeconds store = self.storeUnderTest() for uid in (u"testgroup", u"emptygroup",): config.AutomaticPurging.GroupPurgeIntervalSeconds = oldGroupPurgeIntervalSeconds txn = store.newTransaction() group = yield txn.groupByUID(uid) yield txn.addDelegateGroup(delegator=u"sagen", delegateGroupID=group.groupID, readWrite=True) group = yield txn.groupByUID(uid, create=False) yield txn.commit() self.assertNotEqual(group, None) self.assertTrue(group.extant) # Remove the group, still cached yield self.directory.removeRecords([uid]) txn = store.newTransaction() yield self.groupCacher.update(txn) group = yield txn.groupByUID(uid, create=False) yield txn.commit() yield JobItem.waitEmpty(store.newTransaction, reactor, 60) txn = store.newTransaction() group = yield txn.groupByUID(uid, create=False) yield txn.commit() self.assertNotEqual(group, None) self.assertFalse(group.extant) # delete the group config.AutomaticPurging.GroupPurgeIntervalSeconds = "0.0" txn = store.newTransaction() yield self.groupCacher.update(txn) group = yield txn.groupByUID(uid, create=False) yield txn.commit() self.assertEqual(group, None)
def test_group_change_removed_calendar(self): """ Test that group shares are properly cleaned when a calendar is removed (and not trashed). """ self.patch(config, "EnableTrashCollection", False) @inlineCallbacks def expandedMembers(self, records=None, seen=None): if self.uid == "group02": returnValue(frozenset()) 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(), "group02") self.assertEqual(len(wps), 0) yield self._check_notifications("user01", []) # New calendar for sharing home = yield self.homeUnderTest(name="user01") yield home.createCalendarWithName("shared") yield self.commit() # Invite calendar = yield self.calendarUnderTest(home="user01", name="shared") invites = yield calendar.sharingInvites() self.assertEqual(len(invites), 0) self.assertFalse(calendar.isShared()) yield self._check_notifications("user01", []) shareeViews = yield calendar.inviteUIDToShare("group02", _BIND_MODE_READ) self.assertEqual(len(shareeViews), 3) calendar = yield self.calendarUnderTest(home="user01", name="shared") invites = yield calendar.sharingInvites() self.assertEqual(len(invites), 3) 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, ]) groupsToRefresh = yield groupCacher.groupsToRefresh(self.transactionUnderTest()) self.assertEqual(len(groupsToRefresh), 1) yield self.commit() # Remove the collection calendar = yield self.calendarUnderTest(home="user01", name="shared") remove_id = calendar.id() yield calendar.remove() yield self.commit() home = yield self.homeUnderTest(name="user01") calendar = yield home.childWithID(remove_id) self.assertTrue(calendar is None) calendar = yield home.childWithID(remove_id, onlyInTrash=True) self.assertTrue(calendar is None) yield self.commit() # 1 group member self.patch(CalendarDirectoryRecordMixin, "expandedMembers", expandedMembers) wps = yield groupCacher.refreshGroup(self.transactionUnderTest(), "group02") self.assertEqual(len(wps), 0) yield self.commit() yield JobItem.waitEmpty(self._sqlCalendarStore.newTransaction, reactor, 60)
def test_multiple_groups_with_individual_remove_from_one_group(self): """ Test that a multi-group share each containing the same user still lists the user when they are removed from one group. """ @inlineCallbacks def expandedMembers(self, records=None, seen=None): if self.uid == "group05": returnValue(frozenset()) 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) wps = yield groupCacher.refreshGroup(self.transactionUnderTest(), "group06") self.assertEqual(len(wps), 0) # Invite calendar = yield self.calendarUnderTest(home="user03", name="calendar") invites = yield calendar.sharingInvites() self.assertEqual(len(invites), 0) self.assertFalse(calendar.isShared()) shareeView = yield calendar.inviteUIDToShare("user01", _BIND_MODE_READ) self.assertTrue(shareeView is not None) shareeViews = yield calendar.inviteUIDToShare("group05", _BIND_MODE_WRITE) self.assertEqual(len(shareeViews), 2) shareeViews = yield calendar.inviteUIDToShare("group06", _BIND_MODE_READ) self.assertEqual(len(shareeViews), 1) calendar = yield self.calendarUnderTest(home="user03", name="calendar") invites = yield calendar.sharingInvites() self.assertEqual(len(invites), 2) for invite in invites: shareeView = yield calendar.shareeView(invite.shareeUID) self.assertEqual(invite.ownerUID, "user03") self.assertEqual(invite.uid, shareeView.shareName()) if invite.shareeUID == "user01": self.assertEqual(invite.mode, _BIND_MODE_GROUP_READ) else: self.assertEqual(invite.mode, _BIND_MODE_GROUP) self.assertEqual((yield shareeView.effectiveShareMode()), _BIND_MODE_WRITE) self.assertEqual(invite.status, _BIND_STATUS_INVITED) self.assertEqual(invite.summary, None) yield self._check_notifications(invite.shareeUID, [invite.uid, ]) groupsToRefresh = yield groupCacher.groupsToRefresh(self.transactionUnderTest()) self.assertEqual(len(groupsToRefresh), 2) # Change group membership 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="user03", name="calendar") invites = yield calendar.sharingInvites() self.assertEqual(len(invites), 2) for invite in invites: shareeView = yield calendar.shareeView(invite.shareeUID) self.assertEqual(invite.ownerUID, "user03") self.assertEqual(invite.uid, shareeView.shareName()) if invite.shareeUID == "user01": self.assertEqual(invite.mode, _BIND_MODE_READ) else: 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, ]) # Uninvite calendar = yield self.calendarUnderTest(home="user03", name="calendar") yield calendar.uninviteUIDFromShare("user01") yield calendar.uninviteUIDFromShare("group05") yield calendar.uninviteUIDFromShare("group06") noinvites = yield calendar.sharingInvites() self.assertEqual(len(noinvites), 0)
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)
def test_multiple_groups_with_individual_remove_from_one_group(self): """ Test that a multi-group share each containing the same user still lists the user when they are removed from one group. """ @inlineCallbacks def expandedMembers(self, records=None, seen=None): if self.uid == "group05": returnValue(frozenset()) 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) wps = yield groupCacher.refreshGroup(self.transactionUnderTest(), "group06") self.assertEqual(len(wps), 0) # Invite calendar = yield self.calendarUnderTest(home="user03", name="calendar") invites = yield calendar.sharingInvites() self.assertEqual(len(invites), 0) self.assertFalse(calendar.isSharedByOwner()) shareeView = yield calendar.inviteUIDToShare("user01", _BIND_MODE_READ) self.assertTrue(shareeView is not None) shareeViews = yield calendar.inviteUIDToShare("group05", _BIND_MODE_WRITE) self.assertEqual(len(shareeViews), 2) shareeViews = yield calendar.inviteUIDToShare("group06", _BIND_MODE_READ) self.assertEqual(len(shareeViews), 1) calendar = yield self.calendarUnderTest(home="user03", name="calendar") invites = yield calendar.sharingInvites() self.assertEqual(len(invites), 2) for invite in invites: shareeView = yield calendar.shareeView(invite.shareeUID) self.assertEqual(invite.ownerUID, "user03") self.assertEqual(invite.uid, shareeView.shareName()) if invite.shareeUID == "user01": self.assertEqual(invite.mode, _BIND_MODE_GROUP_READ) else: self.assertEqual(invite.mode, _BIND_MODE_GROUP) self.assertEqual((yield shareeView.effectiveShareMode()), _BIND_MODE_WRITE) self.assertEqual(invite.status, _BIND_STATUS_INVITED) self.assertEqual(invite.summary, None) yield self._check_notifications(invite.shareeUID, [ invite.uid, ]) groupsToRefresh = yield groupCacher.groupsToRefresh( self.transactionUnderTest()) self.assertEqual(len(groupsToRefresh), 2) # Change group membership 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="user03", name="calendar") invites = yield calendar.sharingInvites() self.assertEqual(len(invites), 2) for invite in invites: shareeView = yield calendar.shareeView(invite.shareeUID) self.assertEqual(invite.ownerUID, "user03") self.assertEqual(invite.uid, shareeView.shareName()) if invite.shareeUID == "user01": self.assertEqual(invite.mode, _BIND_MODE_READ) else: 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, ]) # Uninvite calendar = yield self.calendarUnderTest(home="user03", name="calendar") yield calendar.uninviteUIDFromShare("user01") yield calendar.uninviteUIDFromShare("group05") yield calendar.uninviteUIDFromShare("group06") noinvites = yield calendar.sharingInvites() self.assertEqual(len(noinvites), 0)
class GroupCacherTest(StoreTestCase): @inlineCallbacks def setUp(self): yield super(GroupCacherTest, self).setUp() self.groupCacher = GroupCacher(self.directory) @inlineCallbacks def test_multipleCalls(self): """ Ensure multiple calls to groupByUID() don't raise an exception """ store = self.storeUnderTest() txn = store.newTransaction() record = yield self.directory.recordWithUID(u"__top_group_1__") yield txn.groupByUID(record.uid) yield txn.groupByUID(record.uid) yield txn.commit() @inlineCallbacks def test_refreshGroup(self): """ Verify refreshGroup() adds a group to the Groups table with the expected membership hash value and members """ store = self.storeUnderTest() txn = store.newTransaction() record = yield self.directory.recordWithUID(u"__top_group_1__") yield self.groupCacher.refreshGroup(txn, record.uid) (groupID, _ignore_name, membershipHash, _ignore_modified, extant) = (yield txn.groupByUID(record.uid)) self.assertEquals(extant, True) self.assertEquals(membershipHash, "553eb54e3bbb26582198ee04541dbee4") groupUID, name, membershipHash, extant = (yield txn.groupByID(groupID)) self.assertEquals(groupUID, record.uid) self.assertEquals(name, u"Top Group 1") self.assertEquals(membershipHash, "553eb54e3bbb26582198ee04541dbee4") self.assertEquals(extant, True) members = (yield txn.membersOfGroup(groupID)) self.assertEquals( set([ u'__cdaboo1__', u'__glyph1__', u'__sagen1__', u'__wsanchez1__' ]), members) records = (yield self.groupCacher.cachedMembers(txn, groupID)) self.assertEquals( set([r.uid for r in records]), set([ u'__cdaboo1__', u'__glyph1__', u'__sagen1__', u'__wsanchez1__' ])) # sagen is in the top group, even though it's actually one level # removed record = yield self.directory.recordWithUID(u"__sagen1__") groups = (yield self.groupCacher.cachedGroupsFor(txn, record.uid)) self.assertEquals(set([u"__top_group_1__"]), groups) yield txn.commit() @inlineCallbacks def test_synchronizeMembers(self): """ After loading in a group via refreshGroup(), pass new member sets to synchronizeMembers() and verify members are added and removed as expected """ store = self.storeUnderTest() txn = store.newTransaction() # Refresh the group so it's assigned a group_id uid = u"__top_group_1__" yield self.groupCacher.refreshGroup(txn, uid) (groupID, name, _ignore_membershipHash, _ignore_modified, _ignore_extant) = yield txn.groupByUID(uid) # Remove two members, and add one member newSet = set() for name in (u"wsanchez1", u"cdaboo1", u"dre1"): record = (yield self.directory.recordWithShortName( RecordType.user, name)) newSet.add(record.uid) numAdded, numRemoved = (yield self.groupCacher.synchronizeMembers( txn, groupID, newSet)) self.assertEquals(numAdded, 1) self.assertEquals(numRemoved, 2) records = (yield self.groupCacher.cachedMembers(txn, groupID)) self.assertEquals(set([r.shortNames[0] for r in records]), set(["wsanchez1", "cdaboo1", "dre1"])) # Remove all members numAdded, numRemoved = (yield self.groupCacher.synchronizeMembers( txn, groupID, set())) self.assertEquals(numAdded, 0) self.assertEquals(numRemoved, 3) records = (yield self.groupCacher.cachedMembers(txn, groupID)) self.assertEquals(len(records), 0) yield txn.commit() @inlineCallbacks def test_groupByID(self): store = self.storeUnderTest() txn = store.newTransaction() # Non-existent groupID self.failUnlessFailure(txn.groupByID(42), NotFoundError) uid = u"__top_group_1__" hash = "553eb54e3bbb26582198ee04541dbee4" yield self.groupCacher.refreshGroup(txn, uid) (groupID, _ignore_name, _ignore_membershipHash, _ignore_modified, extant) = yield txn.groupByUID(uid) results = yield txn.groupByID(groupID) self.assertEquals((uid, u"Top Group 1", hash, True), results) yield txn.commit() @inlineCallbacks def test_externalAssignments(self): store = self.storeUnderTest() txn = store.newTransaction() oldExternalAssignments = (yield txn.externalDelegates()) self.assertEquals(oldExternalAssignments, {}) newAssignments = {u"__wsanchez1__": (None, u"__top_group_1__")} yield self.groupCacher.applyExternalAssignments(txn, newAssignments) oldExternalAssignments = (yield txn.externalDelegates()) self.assertEquals(oldExternalAssignments, {u"__wsanchez1__": (None, u"__top_group_1__")}) newAssignments = { u"__cdaboo1__": (u"__sub_group_1__", None), u"__wsanchez1__": (u"__sub_group_1__", u"__top_group_1__"), } yield self.groupCacher.applyExternalAssignments(txn, newAssignments) oldExternalAssignments = (yield txn.externalDelegates()) self.assertEquals( oldExternalAssignments, { u"__wsanchez1__": (u"__sub_group_1__", u"__top_group_1__"), u"__cdaboo1__": (u"__sub_group_1__", None) }) allGroupDelegates = (yield txn.allGroupDelegates()) self.assertEquals(allGroupDelegates, set([u"__top_group_1__", u"__sub_group_1__"])) # Fault in the read-only group yield self.groupCacher.refreshGroup(txn, u"__sub_group_1__") # Wilfredo should have Sagen and Daboo as read-only delegates delegates = (yield txn.delegates(u"__wsanchez1__", False, expanded=True)) self.assertEquals(delegates, set([u"__sagen1__", u"__cdaboo1__"])) # Fault in the read-write group yield self.groupCacher.refreshGroup(txn, u"__top_group_1__") # Wilfredo should have 4 users as read-write delegates delegates = (yield txn.delegates(u"__wsanchez1__", True, expanded=True)) self.assertEquals( delegates, set([ u"__wsanchez1__", u"__sagen1__", u"__cdaboo1__", u"__glyph1__" ])) # # Now, remove some external assignments # newAssignments = { u"__wsanchez1__": (u"__sub_group_1__", None), } yield self.groupCacher.applyExternalAssignments(txn, newAssignments) oldExternalAssignments = (yield txn.externalDelegates()) self.assertEquals(oldExternalAssignments, { u"__wsanchez1__": (u"__sub_group_1__", None), }) allGroupDelegates = (yield txn.allGroupDelegates()) self.assertEquals(allGroupDelegates, set([u"__sub_group_1__"])) # Wilfredo should have Sagen and Daboo as read-only delegates delegates = (yield txn.delegates(u"__wsanchez1__", False, expanded=True)) self.assertEquals(delegates, set([u"__sagen1__", u"__cdaboo1__"])) # Wilfredo should have no read-write delegates delegates = (yield txn.delegates(u"__wsanchez1__", True, expanded=True)) self.assertEquals(delegates, set([])) # Only 1 group as delegate now: allGroupDelegates = (yield txn.allGroupDelegates()) self.assertEquals(allGroupDelegates, set([u"__sub_group_1__"])) yield txn.commit() def test_diffAssignments(self): """ Ensure external proxy assignment diffing works """ self.assertEquals( ( # changed [], # removed [], ), diffAssignments( # old {}, # new {})) self.assertEquals( ( # changed [], # removed [], ), diffAssignments( # old {"B": ("1", "2")}, # new {"B": ("1", "2")}, )) self.assertEquals( ( # changed [("A", ("1", "2")), ("B", ("3", "4"))], # removed [], ), diffAssignments( # old {}, # new { "A": ("1", "2"), "B": ("3", "4") })) self.assertEquals( ( # changed [], # removed ["A", "B"], ), diffAssignments( # old { "A": ("1", "2"), "B": ("3", "4") }, # new {}, )) self.assertEquals( ( # changed [('C', ('4', '5')), ('D', ('7', '8'))], # removed ["B"], ), diffAssignments( # old { "A": ("1", "2"), "B": ("3", "4"), "C": ("5", "6") }, # new { "D": ("7", "8"), "C": ("4", "5"), "A": ("1", "2") }, ))
class DynamicGroupTest(StoreTestCase): @inlineCallbacks def setUp(self): yield super(DynamicGroupTest, self).setUp() self.directory = CalendarInMemoryDirectoryService(None) self.store.setDirectoryService(self.directory) self.groupCacher = GroupCacher(self.directory) self.numUsers = 100 # Add users records = [] fieldName = self.directory.fieldName for i in xrange(self.numUsers): records.append( TestRecord( self.directory, { fieldName.uid: u"foo{ctr:05d}".format(ctr=i), fieldName.shortNames: (u"foo{ctr:05d}".format(ctr=i),), fieldName.fullNames: (u"foo{ctr:05d}".format(ctr=i),), fieldName.recordType: RecordType.user, } ) ) # Add a group records.append( TestRecord( self.directory, { fieldName.uid: u"testgroup", fieldName.recordType: RecordType.group, } ) ) yield self.directory.updateRecords(records, create=True) group = yield self.directory.recordWithUID(u"testgroup") members = yield self.directory.recordsWithRecordType(RecordType.user) yield group.setMembers(members) @inlineCallbacks def test_extant(self): """ Verify that once a group is removed from the directory, the next call to refreshGroup() will set the "extent" to False. Add the group back to the directory and "extent" becomes True. """ store = self.storeUnderTest() txn = store.newTransaction() yield self.groupCacher.refreshGroup(txn, u"testgroup") ( groupID, _ignore_name, membershipHash, _ignore_modified, extant ) = (yield txn.groupByUID(u"testgroup")) yield txn.commit() self.assertTrue(extant) # Remove the group yield self.directory.removeRecords([u"testgroup"]) txn = store.newTransaction() yield self.groupCacher.refreshGroup(txn, u"testgroup") ( groupID, _ignore_name, membershipHash, _ignore_modified, extant ) = (yield txn.groupByUID(u"testgroup")) yield txn.commit() # Extant = False self.assertFalse(extant) # The list of members stored in the DB for this group is now empty txn = store.newTransaction() members = yield txn.membersOfGroup(groupID) yield txn.commit() self.assertEquals(members, set()) # Add the group back into the directory fieldName = self.directory.fieldName yield self.directory.updateRecords( ( TestRecord( self.directory, { fieldName.uid: u"testgroup", fieldName.recordType: RecordType.group, } ), ), create=True ) group = yield self.directory.recordWithUID(u"testgroup") members = yield self.directory.recordsWithRecordType(RecordType.user) yield group.setMembers(members) txn = store.newTransaction() yield self.groupCacher.refreshGroup(txn, u"testgroup") ( groupID, _ignore_name, membershipHash, _ignore_modified, extant ) = (yield txn.groupByUID(u"testgroup")) yield txn.commit() # Extant = True self.assertTrue(extant) # The list of members stored in the DB for this group has 100 users txn = store.newTransaction() members = yield txn.membersOfGroup(groupID) yield txn.commit() self.assertEquals(len(members), 100)
class GroupCacherTest(StoreTestCase): @inlineCallbacks def setUp(self): yield super(GroupCacherTest, self).setUp() self.groupCacher = GroupCacher(self.directory) @inlineCallbacks def test_multipleCalls(self): """ Ensure multiple calls to groupByUID() don't raise an exception """ store = self.storeUnderTest() txn = store.newTransaction() record = yield self.directory.recordWithUID(u"__top_group_1__") yield txn.groupByUID(record.uid) yield txn.groupByUID(record.uid) yield txn.commit() @inlineCallbacks def test_refreshGroup(self): """ Verify refreshGroup() adds a group to the Groups table with the expected membership hash value and members """ store = self.storeUnderTest() txn = store.newTransaction() record = yield self.directory.recordWithUID(u"__top_group_1__") yield self.groupCacher.refreshGroup(txn, record.uid) ( groupID, _ignore_name, membershipHash, _ignore_modified, extant ) = (yield txn.groupByUID(record.uid)) self.assertEquals(extant, True) self.assertEquals(membershipHash, "553eb54e3bbb26582198ee04541dbee4") groupUID, name, membershipHash, extant = (yield txn.groupByID(groupID)) self.assertEquals(groupUID, record.uid) self.assertEquals(name, u"Top Group 1") self.assertEquals(membershipHash, "553eb54e3bbb26582198ee04541dbee4") self.assertEquals(extant, True) members = (yield txn.membersOfGroup(groupID)) self.assertEquals( set([u'__cdaboo1__', u'__glyph1__', u'__sagen1__', u'__wsanchez1__']), members ) records = (yield self.groupCacher.cachedMembers(txn, groupID)) self.assertEquals( set([r.uid for r in records]), set([u'__cdaboo1__', u'__glyph1__', u'__sagen1__', u'__wsanchez1__']) ) # sagen is in the top group, even though it's actually one level # removed record = yield self.directory.recordWithUID(u"__sagen1__") groups = (yield self.groupCacher.cachedGroupsFor(txn, record.uid)) self.assertEquals(set([u"__top_group_1__"]), groups) yield txn.commit() @inlineCallbacks def test_synchronizeMembers(self): """ After loading in a group via refreshGroup(), pass new member sets to synchronizeMembers() and verify members are added and removed as expected """ store = self.storeUnderTest() txn = store.newTransaction() # Refresh the group so it's assigned a group_id uid = u"__top_group_1__" yield self.groupCacher.refreshGroup(txn, uid) ( groupID, name, _ignore_membershipHash, _ignore_modified, _ignore_extant ) = yield txn.groupByUID(uid) # Remove two members, and add one member newSet = set() for name in (u"wsanchez1", u"cdaboo1", u"dre1"): record = ( yield self.directory.recordWithShortName( RecordType.user, name ) ) newSet.add(record.uid) numAdded, numRemoved = ( yield self.groupCacher.synchronizeMembers( txn, groupID, newSet ) ) self.assertEquals(numAdded, 1) self.assertEquals(numRemoved, 2) records = (yield self.groupCacher.cachedMembers(txn, groupID)) self.assertEquals( set([r.shortNames[0] for r in records]), set(["wsanchez1", "cdaboo1", "dre1"]) ) # Remove all members numAdded, numRemoved = ( yield self.groupCacher.synchronizeMembers(txn, groupID, set()) ) self.assertEquals(numAdded, 0) self.assertEquals(numRemoved, 3) records = (yield self.groupCacher.cachedMembers(txn, groupID)) self.assertEquals(len(records), 0) yield txn.commit() @inlineCallbacks def test_groupByID(self): store = self.storeUnderTest() txn = store.newTransaction() # Non-existent groupID self.failUnlessFailure(txn.groupByID(42), NotFoundError) uid = u"__top_group_1__" hash = "553eb54e3bbb26582198ee04541dbee4" yield self.groupCacher.refreshGroup(txn, uid) ( groupID, _ignore_name, _ignore_membershipHash, _ignore_modified, extant ) = yield txn.groupByUID(uid) results = yield txn.groupByID(groupID) self.assertEquals((uid, u"Top Group 1", hash, True), results) yield txn.commit() @inlineCallbacks def test_externalAssignments(self): store = self.storeUnderTest() txn = store.newTransaction() oldExternalAssignments = (yield txn.externalDelegates()) self.assertEquals(oldExternalAssignments, {}) newAssignments = { u"__wsanchez1__": (None, u"__top_group_1__") } yield self.groupCacher.applyExternalAssignments(txn, newAssignments) oldExternalAssignments = (yield txn.externalDelegates()) self.assertEquals( oldExternalAssignments, { u"__wsanchez1__": ( None, u"__top_group_1__" ) } ) newAssignments = { u"__cdaboo1__": ( u"__sub_group_1__", None ), u"__wsanchez1__": ( u"__sub_group_1__", u"__top_group_1__" ), } yield self.groupCacher.applyExternalAssignments(txn, newAssignments) oldExternalAssignments = (yield txn.externalDelegates()) self.assertEquals( oldExternalAssignments, { u"__wsanchez1__": ( u"__sub_group_1__", u"__top_group_1__" ), u"__cdaboo1__": ( u"__sub_group_1__", None ) } ) allGroupDelegates = (yield txn.allGroupDelegates()) self.assertEquals( allGroupDelegates, set( [ u"__top_group_1__", u"__sub_group_1__" ] ) ) # Fault in the read-only group yield self.groupCacher.refreshGroup(txn, u"__sub_group_1__") # Wilfredo should have Sagen and Daboo as read-only delegates delegates = (yield txn.delegates( u"__wsanchez1__", False, expanded=True) ) self.assertEquals( delegates, set( [ u"__sagen1__", u"__cdaboo1__" ] ) ) # Fault in the read-write group yield self.groupCacher.refreshGroup(txn, u"__top_group_1__") # Wilfredo should have 4 users as read-write delegates delegates = (yield txn.delegates( u"__wsanchez1__", True, expanded=True) ) self.assertEquals( delegates, set( [ u"__wsanchez1__", u"__sagen1__", u"__cdaboo1__", u"__glyph1__" ] ) ) # # Now, remove some external assignments # newAssignments = { u"__wsanchez1__": ( u"__sub_group_1__", None ), } yield self.groupCacher.applyExternalAssignments(txn, newAssignments) oldExternalAssignments = (yield txn.externalDelegates()) self.assertEquals( oldExternalAssignments, { u"__wsanchez1__": ( u"__sub_group_1__", None ), } ) allGroupDelegates = (yield txn.allGroupDelegates()) self.assertEquals( allGroupDelegates, set( [ u"__sub_group_1__" ] ) ) # Wilfredo should have Sagen and Daboo as read-only delegates delegates = (yield txn.delegates( u"__wsanchez1__", False, expanded=True) ) self.assertEquals( delegates, set( [ u"__sagen1__", u"__cdaboo1__" ] ) ) # Wilfredo should have no read-write delegates delegates = (yield txn.delegates( u"__wsanchez1__", True, expanded=True) ) self.assertEquals( delegates, set([]) ) # Only 1 group as delegate now: allGroupDelegates = (yield txn.allGroupDelegates()) self.assertEquals( allGroupDelegates, set( [ u"__sub_group_1__" ] ) ) yield txn.commit() def test_diffAssignments(self): """ Ensure external proxy assignment diffing works """ self.assertEquals( ( # changed [], # removed [], ), diffAssignments( # old {}, # new {} ) ) self.assertEquals( ( # changed [], # removed [], ), diffAssignments( # old {"B": ("1", "2")}, # new {"B": ("1", "2")}, ) ) self.assertEquals( ( # changed [("A", ("1", "2")), ("B", ("3", "4"))], # removed [], ), diffAssignments( # old {}, # new {"A": ("1", "2"), "B": ("3", "4")} ) ) self.assertEquals( ( # changed [], # removed ["A", "B"], ), diffAssignments( # old {"A": ("1", "2"), "B": ("3", "4")}, # new {}, ) ) self.assertEquals( ( # changed [('C', ('4', '5')), ('D', ('7', '8'))], # removed ["B"], ), diffAssignments( # old {"A": ("1", "2"), "B": ("3", "4"), "C": ("5", "6")}, # new {"D": ("7", "8"), "C": ("4", "5"), "A": ("1", "2")}, ) )
class DelegationTest(StoreTestCase): @inlineCallbacks def setUp(self): yield super(DelegationTest, self).setUp() self.store = self.storeUnderTest() self.groupCacher = GroupCacher(self.directory) yield Delegates._memcacher.flushAll() @inlineCallbacks def test_recordCreation(self): """ Verify the record we get back from recordWithShortName has a shortName that matches the one we looked up. """ record = yield self.directory.recordWithShortName( DelegateRecordType.readDelegateGroup, "foo" ) self.assertEquals(record.shortNames[0], "foo") @inlineCallbacks def test_directDelegation(self): txn = self.store.newTransaction(label="test_directDelegation") delegator = yield self.directory.recordWithUID(u"__wsanchez1__") delegate1 = yield self.directory.recordWithUID(u"__sagen1__") delegate2 = yield self.directory.recordWithUID(u"__cdaboo1__") # Add 1 delegate yield Delegates.addDelegate(txn, delegator, delegate1, True) delegates = (yield Delegates.delegatesOf(txn, delegator, True)) self.assertEquals([u"__sagen1__"], [d.uid for d in delegates]) delegators = (yield Delegates.delegatedTo(txn, delegate1, True)) self.assertEquals([u"__wsanchez1__"], [d.uid for d in delegators]) yield txn.commit() # So delegateService will see the changes txn = self.store.newTransaction(label="test_directDelegation") # The "proxy-write" pseudoGroup will have one member pseudoGroup = yield self.directory.recordWithShortName( DelegateRecordType.writeDelegateGroup, u"__wsanchez1__" ) self.assertEquals(pseudoGroup.uid, u"__wsanchez1__#calendar-proxy-write") self.assertEquals( [r.uid for r in (yield pseudoGroup.members())], [u"__sagen1__"] ) # The "proxy-read" pseudoGroup will have no members pseudoGroup = yield self.directory.recordWithShortName( DelegateRecordType.readDelegateGroup, u"__wsanchez1__" ) self.assertEquals(pseudoGroup.uid, u"__wsanchez1__#calendar-proxy-read") self.assertEquals( [r.uid for r in (yield pseudoGroup.members())], [] ) # The "proxy-write-for" pseudoGroup will have one member pseudoGroup = yield self.directory.recordWithShortName( DelegateRecordType.writeDelegatorGroup, u"__sagen1__" ) self.assertEquals(pseudoGroup.uid, u"__sagen1__#calendar-proxy-write-for") self.assertEquals( [r.uid for r in (yield pseudoGroup.members())], [u"__wsanchez1__"] ) # The "proxy-read-for" pseudoGroup will have no members pseudoGroup = yield self.directory.recordWithShortName( DelegateRecordType.readDelegatorGroup, u"__sagen1__" ) self.assertEquals(pseudoGroup.uid, u"__sagen1__#calendar-proxy-read-for") self.assertEquals( [r.uid for r in (yield pseudoGroup.members())], [] ) # Add another delegate yield Delegates.addDelegate(txn, delegator, delegate2, True) delegates = (yield Delegates.delegatesOf(txn, delegator, True)) self.assertEquals( set([u"__sagen1__", u"__cdaboo1__"]), set([d.uid for d in delegates]) ) delegators = (yield Delegates.delegatedTo(txn, delegate2, True)) self.assertEquals([u"__wsanchez1__"], [d.uid for d in delegators]) # Remove 1 delegate yield Delegates.removeDelegate(txn, delegator, delegate1, True) delegates = (yield Delegates.delegatesOf(txn, delegator, True)) self.assertEquals([u"__cdaboo1__"], [d.uid for d in delegates]) delegators = (yield Delegates.delegatedTo(txn, delegate1, True)) self.assertEquals(0, len(delegators)) # Remove the other delegate yield Delegates.removeDelegate(txn, delegator, delegate2, True) delegates = (yield Delegates.delegatesOf(txn, delegator, True)) self.assertEquals(0, len(delegates)) delegators = (yield Delegates.delegatedTo(txn, delegate2, True)) self.assertEquals(0, len(delegators)) yield txn.commit() # So delegateService will see the changes # Now set delegate assignments by using pseudoGroup.setMembers() pseudoGroup = yield self.directory.recordWithShortName( DelegateRecordType.writeDelegateGroup, u"__wsanchez1__" ) yield pseudoGroup.setMembers([delegate1, delegate2]) # Verify the assignments were made txn = self.store.newTransaction(label="test_directDelegation") delegates = (yield Delegates.delegatesOf(txn, delegator, True)) self.assertEquals( set([u"__sagen1__", u"__cdaboo1__"]), set([d.uid for d in delegates]) ) yield txn.commit() # Set a different group of assignments: yield pseudoGroup.setMembers([delegate2]) # Verify the assignments were made txn = self.store.newTransaction(label="test_directDelegation") delegates = (yield Delegates.delegatesOf(txn, delegator, True)) self.assertEquals( set([u"__cdaboo1__"]), set([d.uid for d in delegates]) ) yield txn.commit() @inlineCallbacks def test_indirectDelegation(self): txn = self.store.newTransaction(label="test_indirectDelegation") delegator = yield self.directory.recordWithUID(u"__wsanchez1__") delegate1 = yield self.directory.recordWithUID(u"__sagen1__") group1 = yield self.directory.recordWithUID(u"__top_group_1__") group2 = yield self.directory.recordWithUID(u"__sub_group_1__") # Add group delegate yield Delegates.addDelegate(txn, delegator, group1, True) # Passing expanded=False will return the group delegates = (yield Delegates.delegatesOf(txn, delegator, True, expanded=False)) self.assertEquals(1, len(delegates)) self.assertEquals(delegates[0].uid, u"__top_group_1__") # Passing expanded=True will return not the group -- it only returns # non-groups delegates = (yield Delegates.delegatesOf(txn, delegator, True, expanded=True)) self.assertEquals( set([u"__sagen1__", u"__cdaboo1__", u"__glyph1__"]), set([d.uid for d in delegates]) ) delegators = (yield Delegates.delegatedTo(txn, delegate1, True)) self.assertEquals([u"__wsanchez1__"], [d.uid for d in delegators]) # Verify we can ask for all delegated-to groups yield Delegates.addDelegate(txn, delegator, group2, True) groups = (yield txn.allGroupDelegates()) self.assertEquals( set([u'__sub_group_1__', u'__top_group_1__']), set(groups) ) # Delegate to a user who is already indirectly delegated-to yield Delegates.addDelegate(txn, delegator, delegate1, True) delegates = (yield Delegates.delegatesOf(txn, delegator, True, expanded=True)) self.assertEquals( set([u"__sagen1__", u"__cdaboo1__", u"__glyph1__"]), set([d.uid for d in delegates]) ) # Add a member to the group; they become a delegate newSet = set() for name in (u"wsanchez1", u"cdaboo1", u"sagen1", u"glyph1", u"dre1"): record = ( yield self.directory.recordWithShortName(RecordType.user, name) ) newSet.add(record.uid) group = yield txn.groupByUID(group1.uid) _ignore_added, _ignore_removed = ( yield self.groupCacher.synchronizeMembers(txn, group.groupID, newSet) ) delegates = (yield Delegates.delegatesOf(txn, delegator, True, expanded=True)) self.assertEquals( set([u"__sagen1__", u"__cdaboo1__", u"__glyph1__", u"__dre1__"]), set([d.uid for d in delegates]) ) # Remove delegate access from the top group yield Delegates.removeDelegate(txn, delegator, group1, True) delegates = (yield Delegates.delegatesOf(txn, delegator, True, expanded=True)) self.assertEquals( set([u"__sagen1__", u"__cdaboo1__"]), set([d.uid for d in delegates]) ) # Remove delegate access from the sub group yield Delegates.removeDelegate(txn, delegator, group2, True) delegates = (yield Delegates.delegatesOf(txn, delegator, True, expanded=True)) self.assertEquals( set([u"__sagen1__"]), set([d.uid for d in delegates]) ) yield txn.commit() @inlineCallbacks def test_noDuplication(self): """ Make sure addDelegate( ) is idempotent """ delegator = yield self.directory.recordWithUID(u"__wsanchez1__") # Delegate users: delegate1 = yield self.directory.recordWithUID(u"__sagen1__") txn = self.store.newTransaction(label="test_noDuplication") yield Delegates.addDelegate(txn, delegator, delegate1, True) yield txn.commit() txn = self.store.newTransaction(label="test_noDuplication") yield Delegates.addDelegate(txn, delegator, delegate1, True) yield txn.commit() txn = self.store.newTransaction(label="test_noDuplication") results = yield DelegateRecord.query( txn, (DelegateRecord.delegator == delegator.uid.encode("utf-8")).And( DelegateRecord.readWrite == 1 ) ) yield txn.commit() self.assertEquals(["__sagen1__", ], [record.delegate for record in results]) # Delegate groups: group1 = yield self.directory.recordWithUID(u"__top_group_1__") txn = self.store.newTransaction(label="test_noDuplication") yield Delegates.addDelegate(txn, delegator, group1, True) yield txn.commit() txn = self.store.newTransaction(label="test_noDuplication") yield Delegates.addDelegate(txn, delegator, group1, True) yield txn.commit() txn = self.store.newTransaction(label="test_noDuplication") results = yield DelegateGroupsRecord.delegateGroups( txn, delegator.uid, True, ) yield txn.commit() self.assertEquals(["__top_group_1__", ], [record.groupUID for record in results])
def test_group_change_invite_larger(self): """ Test that group shares are changed when the group changes. """ @inlineCallbacks def expandedMembers(self, records=None, seen=None): if self.uid == "group02" or self.uid == "group03": returnValue(frozenset()) else: returnValue((yield unpatchedExpandedMembers(self, records, seen))) unpatchedExpandedMembers = CalendarDirectoryRecordMixin.expandedMembers # 1 group member self.patch(CalendarDirectoryRecordMixin, "expandedMembers", 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(), "group04") 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("group04", _BIND_MODE_READ) self.assertEqual(len(shareeViews), 1) invites = yield calendar.sharingInvites() self.assertEqual(len(invites), 1) for invite in invites: self.assertEqual(invite.shareeUID, "user10") 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, ]) groupsToRefresh = yield groupCacher.groupsToRefresh(self.transactionUnderTest()) self.assertEqual(len(groupsToRefresh), 1) # group members restored self.patch(CalendarDirectoryRecordMixin, "expandedMembers", unpatchedExpandedMembers) wps = yield groupCacher.refreshGroup(self.transactionUnderTest(), "group04") 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), 5) 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, ]) # Uninvite calendar = yield self.calendarUnderTest(home="user01", name="calendar") yield calendar.uninviteUIDFromShare("group04") noinvites = yield calendar.sharingInvites() self.assertEqual(len(noinvites), 0)
def setUp(self): yield super(DelegationTest, self).setUp() self.store = self.storeUnderTest() self.groupCacher = GroupCacher(self.directory) yield Delegates._memcacher.flushAll()
def setUp(self): yield super(AugmentTest, self).setUp() self.groupCacher = GroupCacher(self.directory)
class GroupCacherTest(StoreTestCase): @inlineCallbacks def setUp(self): yield super(GroupCacherTest, self).setUp() self.groupCacher = GroupCacher(self.directory) @inlineCallbacks def test_multipleCalls(self): """ Ensure multiple calls to groupByUID() don't raise an exception """ store = self.storeUnderTest() txn = store.newTransaction() record = yield self.directory.recordWithUID(u"__top_group_1__") yield txn.groupByUID(record.uid) yield txn.groupByUID(record.uid) yield txn.commit() @inlineCallbacks def test_refreshGroup(self): """ Verify refreshGroup() adds a group to the Groups table with the expected membership hash value and members """ store = self.storeUnderTest() txn = store.newTransaction() record = yield self.directory.recordWithUID(u"__top_group_1__") yield self.groupCacher.refreshGroup(txn, record.uid) group = (yield txn.groupByUID(record.uid)) self.assertEquals(group.extant, True) self.assertEquals(group.membershipHash, "553eb54e3bbb26582198ee04541dbee4") group = yield txn.groupByID(group.groupID) self.assertEquals(group.groupUID, record.uid) self.assertEquals(group.name, u"Top Group 1") self.assertEquals(group.membershipHash, "553eb54e3bbb26582198ee04541dbee4") self.assertEquals(group.extant, True) members = (yield txn.groupMemberUIDs(group.groupID)) self.assertEquals( set([ u'__cdaboo1__', u'__glyph1__', u'__sagen1__', u'__wsanchez1__' ]), members) records = (yield self.groupCacher.cachedMembers(txn, group.groupID)) self.assertEquals( set([r.uid for r in records]), set([ u'__cdaboo1__', u'__glyph1__', u'__sagen1__', u'__wsanchez1__' ])) # sagen is in the top group, even though it's actually one level # removed record = yield self.directory.recordWithUID(u"__sagen1__") groups = (yield self.groupCacher.cachedGroupsFor(txn, record.uid)) self.assertEquals(set([u"__top_group_1__"]), groups) yield txn.commit() @inlineCallbacks def test_synchronizeMembers(self): """ After loading in a group via refreshGroup(), pass new member sets to synchronizeMembers() and verify members are added and removed as expected """ store = self.storeUnderTest() txn = store.newTransaction() # Refresh the group so it's assigned a group_id uid = u"__top_group_1__" yield self.groupCacher.refreshGroup(txn, uid) group = yield txn.groupByUID(uid) # Remove two members, and add one member newSet = set() for name in (u"wsanchez1", u"cdaboo1", u"dre1"): record = (yield self.directory.recordWithShortName( RecordType.user, name)) newSet.add(record.uid) added, removed = (yield self.groupCacher.synchronizeMembers( txn, group.groupID, newSet)) self.assertEquals(added, set([ "__dre1__", ])) self.assertEquals(removed, set([ "__glyph1__", "__sagen1__", ])) records = (yield self.groupCacher.cachedMembers(txn, group.groupID)) self.assertEquals(set([r.shortNames[0] for r in records]), set(["wsanchez1", "cdaboo1", "dre1"])) # Remove all members added, removed = (yield self.groupCacher.synchronizeMembers( txn, group.groupID, set())) self.assertEquals(added, set()) self.assertEquals(removed, set([ "__wsanchez1__", "__cdaboo1__", "__dre1__", ])) records = (yield self.groupCacher.cachedMembers(txn, group.groupID)) self.assertEquals(len(records), 0) yield txn.commit() @inlineCallbacks def test_groupByID(self): store = self.storeUnderTest() txn = store.newTransaction() # Non-existent groupID yield self.failUnlessFailure(txn.groupByID(42), NotFoundError) uid = u"__top_group_1__" hash = "553eb54e3bbb26582198ee04541dbee4" yield self.groupCacher.refreshGroup(txn, uid) group = yield txn.groupByUID(uid) group = yield txn.groupByID(group.groupID) self.assertEqual(group.groupUID, uid) self.assertEqual(group.name, u"Top Group 1") self.assertEqual(group.membershipHash, hash) self.assertEqual(group.extant, True) yield txn.commit() @inlineCallbacks def test_externalAssignments(self): store = self.storeUnderTest() txn = store.newTransaction() oldExternalAssignments = (yield txn.externalDelegates()) self.assertEquals(oldExternalAssignments, {}) newAssignments = {u"__wsanchez1__": (None, u"__top_group_1__")} yield self.groupCacher.scheduleExternalAssignments(txn, newAssignments, immediately=True) oldExternalAssignments = (yield txn.externalDelegates()) self.assertEquals(oldExternalAssignments, {u"__wsanchez1__": (None, u"__top_group_1__")}) newAssignments = { u"__cdaboo1__": (u"__sub_group_1__", None), u"__wsanchez1__": (u"__sub_group_1__", u"__top_group_1__"), } yield self.groupCacher.scheduleExternalAssignments(txn, newAssignments, immediately=True) oldExternalAssignments = (yield txn.externalDelegates()) self.assertEquals( oldExternalAssignments, { u"__wsanchez1__": (u"__sub_group_1__", u"__top_group_1__"), u"__cdaboo1__": (u"__sub_group_1__", None) }) allGroupDelegates = (yield txn.allGroupDelegates()) self.assertEquals(allGroupDelegates, set([u"__top_group_1__", u"__sub_group_1__"])) # Fault in the read-only group yield self.groupCacher.refreshGroup(txn, u"__sub_group_1__") # Wilfredo should have Sagen and Daboo as read-only delegates delegates = (yield txn.delegates(u"__wsanchez1__", False, expanded=True)) self.assertEquals(delegates, set([u"__sagen1__", u"__cdaboo1__"])) # Fault in the read-write group yield self.groupCacher.refreshGroup(txn, u"__top_group_1__") # Wilfredo should have 4 users as read-write delegates delegates = (yield txn.delegates(u"__wsanchez1__", True, expanded=True)) self.assertEquals(delegates, set([u"__sagen1__", u"__cdaboo1__", u"__glyph1__"])) # # Now, remove some external assignments # newAssignments = { u"__wsanchez1__": (u"__sub_group_1__", None), } yield self.groupCacher.scheduleExternalAssignments(txn, newAssignments, immediately=True) oldExternalAssignments = (yield txn.externalDelegates()) self.assertEquals(oldExternalAssignments, { u"__wsanchez1__": (u"__sub_group_1__", None), }) allGroupDelegates = (yield txn.allGroupDelegates()) self.assertEquals(allGroupDelegates, set([u"__sub_group_1__"])) # Wilfredo should have Sagen and Daboo as read-only delegates delegates = (yield txn.delegates(u"__wsanchez1__", False, expanded=True)) self.assertEquals(delegates, set([u"__sagen1__", u"__cdaboo1__"])) # Wilfredo should have no read-write delegates delegates = (yield txn.delegates(u"__wsanchez1__", True, expanded=True)) self.assertEquals(delegates, set([])) # Only 1 group as delegate now: allGroupDelegates = (yield txn.allGroupDelegates()) self.assertEquals(allGroupDelegates, set([u"__sub_group_1__"])) # # Say somebody messed up and stuck a non-existent group UID in # as a delegate # newAssignments = { u"__wsanchez1__": ( u"__sub_group_1__", u"__non_existent_group__", ), } yield self.groupCacher.scheduleExternalAssignments(txn, newAssignments, immediately=True) oldExternalAssignments = (yield txn.externalDelegates()) self.assertEquals( oldExternalAssignments, { u"__wsanchez1__": ( u"__sub_group_1__", None # <--- (not __non_existent_group__) ), }) yield txn.commit() def test_diffAssignments(self): """ Ensure external proxy assignment diffing works """ self.assertEquals( ( # changed [], # removed [], ), diffAssignments( # old {}, # new {})) self.assertEquals( ( # changed [], # removed [], ), diffAssignments( # old {"B": ("1", "2")}, # new {"B": ("1", "2")}, )) self.assertEquals( map( set, ( # changed [("A", ("1", "2")), ("B", ("3", "4"))], # removed [], )), map( set, diffAssignments( # old {}, # new { "A": ("1", "2"), "B": ("3", "4") }))) self.assertEquals( map( set, ( # changed [], # removed ["A", "B"], )), map( set, diffAssignments( # old { "A": ("1", "2"), "B": ("3", "4") }, # new {}, ))) self.assertEquals( map( set, ( # changed [('C', ('4', '5')), ('D', ('7', '8'))], # removed ["B"], )), map( set, diffAssignments( # old { "A": ("1", "2"), "B": ("3", "4"), "C": ("5", "6") }, # new { "D": ("7", "8"), "C": ("4", "5"), "A": ("1", "2") }, ))) @inlineCallbacks def test_recursiveGroup(self): """ Verify refreshGroup() adds a group to the Groups table with the expected membership hash value and members """ store = self.storeUnderTest() txn = store.newTransaction() record = yield self.directory.recordWithUID(u"recursive1_coasts") members = yield record.expandedMembers() self.assertEquals( set([r.uid for r in members]), set([ u'6423F94A-6B76-4A3A-815B-D52CFD77935D', u'5A985493-EE2C-4665-94CF-4DFEA3A89500' ])) yield txn.commit() @inlineCallbacks def test_groupChangeCacheNotificationRefreshGroup(self): """ Verify refreshGroup() triggers a cache notification for the group and all members that are added or removed """ class TestNotifier(object): changedTokens = [] def changed(self, token): self.changedTokens.append(token) self.groupCacher.cacheNotifier = TestNotifier() # No change yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__top_group_1__") yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__sub_group_1__") yield self.commit() self.assertEqual(TestNotifier.changedTokens, []) # Add member to group record = yield self.directory.recordWithUID(u"__top_group_1__") addrecord = yield self.directory.recordWithUID(u"__dre1__") yield record.addMembers([ addrecord, ]) yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__top_group_1__") yield self.commit() self.assertEqual(TestNotifier.changedTokens, [ "__top_group_1__", "__dre1__", ]) TestNotifier.changedTokens = [] # Remove member from group record = yield self.directory.recordWithUID(u"__top_group_1__") addrecord = yield self.directory.recordWithUID(u"__dre1__") yield record.removeMembers([ addrecord, ]) yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__top_group_1__") yield self.commit() self.assertEqual(TestNotifier.changedTokens, [ "__top_group_1__", "__dre1__", ]) TestNotifier.changedTokens = [] # Add member to sub-group record = yield self.directory.recordWithUID(u"__sub_group_1__") addrecord = yield self.directory.recordWithUID(u"__dre1__") yield record.addMembers([ addrecord, ]) yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__top_group_1__") yield self.commit() self.assertEqual(TestNotifier.changedTokens, [ "__top_group_1__", "__dre1__", ]) TestNotifier.changedTokens = [] yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__sub_group_1__") yield self.commit() self.assertEqual(TestNotifier.changedTokens, [ "__sub_group_1__", "__dre1__", ]) TestNotifier.changedTokens = [] # Remove member from sub-group record = yield self.directory.recordWithUID(u"__sub_group_1__") addrecord = yield self.directory.recordWithUID(u"__dre1__") yield record.removeMembers([ addrecord, ]) yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__top_group_1__") yield self.commit() self.assertEqual(TestNotifier.changedTokens, [ "__top_group_1__", "__dre1__", ]) TestNotifier.changedTokens = [] yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__sub_group_1__") yield self.commit() self.assertEqual(TestNotifier.changedTokens, [ "__sub_group_1__", "__dre1__", ]) TestNotifier.changedTokens = [] # Remove sub-group member from group record = yield self.directory.recordWithUID(u"__top_group_1__") addrecord = yield self.directory.recordWithUID(u"__sub_group_1__") yield record.removeMembers([ addrecord, ]) yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__top_group_1__") yield self.commit() self.assertEqual( set(TestNotifier.changedTokens), set([ "__top_group_1__", "__sagen1__", "__cdaboo1__", ])) TestNotifier.changedTokens = [] # Add sub-group member to group record = yield self.directory.recordWithUID(u"__top_group_1__") addrecord = yield self.directory.recordWithUID(u"__sub_group_1__") yield record.addMembers([ addrecord, ]) yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__top_group_1__") yield self.commit() self.assertEqual( set(TestNotifier.changedTokens), set([ "__top_group_1__", "__sagen1__", "__cdaboo1__", ])) TestNotifier.changedTokens = [] @inlineCallbacks def test_groupChangeCacheNotificationApplyExternalAssignments(self): """ Verify applyExternalAssignments() triggers a cache notification for the delegator and delegates """ class TestNotifier(object): changedTokens = [] def changed(self, token): self.changedTokens.append(token) self.groupCacher.cacheNotifier = TestNotifier() yield self.groupCacher.applyExternalAssignments( self.transactionUnderTest(), "__dre1__", None, None) yield self.commit() self.assertEqual(TestNotifier.changedTokens, ["__dre1__"]) TestNotifier.changedTokens = [] yield self.groupCacher.applyExternalAssignments( self.transactionUnderTest(), "__dre1__", "__top_group_1__", "__sub_group_1__") yield self.commit() self.assertEqual(TestNotifier.changedTokens, ["__dre1__", "__top_group_1__", "__sub_group_1__"]) TestNotifier.changedTokens = []
class DynamicGroupTest(StoreTestCase): @inlineCallbacks def setUp(self): yield super(DynamicGroupTest, self).setUp() self.directory = CalendarInMemoryDirectoryService(None) self.store.setDirectoryService(self.directory) self.groupCacher = GroupCacher(self.directory) self.numUsers = 100 # Add users records = [] fieldName = self.directory.fieldName for i in xrange(self.numUsers): records.append( TestRecord( self.directory, { fieldName.uid: u"foo{ctr:05d}".format(ctr=i), fieldName.shortNames: (u"foo{ctr:05d}".format(ctr=i), ), fieldName.fullNames: (u"foo{ctr:05d}".format(ctr=i), ), fieldName.recordType: RecordType.user, })) # Add a group records.append( TestRecord( self.directory, { fieldName.uid: u"testgroup", fieldName.recordType: RecordType.group, })) yield self.directory.updateRecords(records, create=True) group = yield self.directory.recordWithUID(u"testgroup") members = yield self.directory.recordsWithRecordType(RecordType.user) yield group.setMembers(members) @inlineCallbacks def test_extant(self): """ Verify that once a group is removed from the directory, the next call to refreshGroup() will set the "extent" to False. Add the group back to the directory and "extent" becomes True. """ store = self.storeUnderTest() txn = store.newTransaction() yield self.groupCacher.refreshGroup(txn, u"testgroup") (groupID, _ignore_name, membershipHash, _ignore_modified, extant) = (yield txn.groupByUID(u"testgroup")) yield txn.commit() self.assertTrue(extant) # Remove the group yield self.directory.removeRecords([u"testgroup"]) txn = store.newTransaction() yield self.groupCacher.refreshGroup(txn, u"testgroup") (groupID, _ignore_name, membershipHash, _ignore_modified, extant) = (yield txn.groupByUID(u"testgroup")) yield txn.commit() # Extant = False self.assertFalse(extant) # The list of members stored in the DB for this group is now empty txn = store.newTransaction() members = yield txn.membersOfGroup(groupID) yield txn.commit() self.assertEquals(members, set()) # Add the group back into the directory fieldName = self.directory.fieldName yield self.directory.updateRecords((TestRecord( self.directory, { fieldName.uid: u"testgroup", fieldName.recordType: RecordType.group, }), ), create=True) group = yield self.directory.recordWithUID(u"testgroup") members = yield self.directory.recordsWithRecordType(RecordType.user) yield group.setMembers(members) txn = store.newTransaction() yield self.groupCacher.refreshGroup(txn, u"testgroup") (groupID, _ignore_name, membershipHash, _ignore_modified, extant) = (yield txn.groupByUID(u"testgroup")) yield txn.commit() # Extant = True self.assertTrue(extant) # The list of members stored in the DB for this group has 100 users txn = store.newTransaction() members = yield txn.membersOfGroup(groupID) yield txn.commit() self.assertEquals(len(members), 100)
class DelegationTest(StoreTestCase): @inlineCallbacks def setUp(self): yield super(DelegationTest, self).setUp() self.store = self.storeUnderTest() self.groupCacher = GroupCacher(self.directory) @inlineCallbacks def test_directDelegation(self): txn = self.store.newTransaction(label="test_directDelegation") delegator = yield self.directory.recordWithUID(u"__wsanchez1__") delegate1 = yield self.directory.recordWithUID(u"__sagen1__") delegate2 = yield self.directory.recordWithUID(u"__cdaboo1__") # Add 1 delegate yield addDelegate(txn, delegator, delegate1, True) delegates = (yield delegatesOf(txn, delegator, True)) self.assertEquals([u"__sagen1__"], [d.uid for d in delegates]) delegators = (yield delegatedTo(txn, delegate1, True)) self.assertEquals([u"__wsanchez1__"], [d.uid for d in delegators]) yield txn.commit() # So delegateService will see the changes txn = self.store.newTransaction(label="test_directDelegation") # The "proxy-write" pseudoGroup will have one member pseudoGroup = yield self.directory.recordWithShortName( DelegateRecordType.writeDelegateGroup, u"__wsanchez1__" ) self.assertEquals(pseudoGroup.uid, u"__wsanchez1__#calendar-proxy-write") self.assertEquals( [r.uid for r in (yield pseudoGroup.members())], [u"__sagen1__"] ) # The "proxy-read" pseudoGroup will have no members pseudoGroup = yield self.directory.recordWithShortName( DelegateRecordType.readDelegateGroup, u"__wsanchez1__" ) self.assertEquals(pseudoGroup.uid, u"__wsanchez1__#calendar-proxy-read") self.assertEquals( [r.uid for r in (yield pseudoGroup.members())], [] ) # The "proxy-write-for" pseudoGroup will have one member pseudoGroup = yield self.directory.recordWithShortName( DelegateRecordType.writeDelegatorGroup, u"__sagen1__" ) self.assertEquals(pseudoGroup.uid, u"__sagen1__#calendar-proxy-write-for") self.assertEquals( [r.uid for r in (yield pseudoGroup.members())], [u"__wsanchez1__"] ) # The "proxy-read-for" pseudoGroup will have no members pseudoGroup = yield self.directory.recordWithShortName( DelegateRecordType.readDelegatorGroup, u"__sagen1__" ) self.assertEquals(pseudoGroup.uid, u"__sagen1__#calendar-proxy-read-for") self.assertEquals( [r.uid for r in (yield pseudoGroup.members())], [] ) # Add another delegate yield addDelegate(txn, delegator, delegate2, True) delegates = (yield delegatesOf(txn, delegator, True)) self.assertEquals( set([u"__sagen1__", u"__cdaboo1__"]), set([d.uid for d in delegates]) ) delegators = (yield delegatedTo(txn, delegate2, True)) self.assertEquals([u"__wsanchez1__"], [d.uid for d in delegators]) # Remove 1 delegate yield removeDelegate(txn, delegator, delegate1, True) delegates = (yield delegatesOf(txn, delegator, True)) self.assertEquals([u"__cdaboo1__"], [d.uid for d in delegates]) delegators = (yield delegatedTo(txn, delegate1, True)) self.assertEquals(0, len(delegators)) # Remove the other delegate yield removeDelegate(txn, delegator, delegate2, True) delegates = (yield delegatesOf(txn, delegator, True)) self.assertEquals(0, len(delegates)) delegators = (yield delegatedTo(txn, delegate2, True)) self.assertEquals(0, len(delegators)) yield txn.commit() # So delegateService will see the changes # Now set delegate assignments by using pseudoGroup.setMembers() pseudoGroup = yield self.directory.recordWithShortName( DelegateRecordType.writeDelegateGroup, u"__wsanchez1__" ) yield pseudoGroup.setMembers([delegate1, delegate2]) # Verify the assignments were made txn = self.store.newTransaction(label="test_directDelegation") delegates = (yield delegatesOf(txn, delegator, True)) self.assertEquals( set([u"__sagen1__", u"__cdaboo1__"]), set([d.uid for d in delegates]) ) yield txn.commit() # Set a different group of assignments: yield pseudoGroup.setMembers([delegate2]) # Verify the assignments were made txn = self.store.newTransaction(label="test_directDelegation") delegates = (yield delegatesOf(txn, delegator, True)) self.assertEquals( set([u"__cdaboo1__"]), set([d.uid for d in delegates]) ) yield txn.commit() @inlineCallbacks def test_indirectDelegation(self): txn = self.store.newTransaction(label="test_indirectDelegation") delegator = yield self.directory.recordWithUID(u"__wsanchez1__") delegate1 = yield self.directory.recordWithUID(u"__sagen1__") group1 = yield self.directory.recordWithUID(u"__top_group_1__") group2 = yield self.directory.recordWithUID(u"__sub_group_1__") # Add group delegate, but before the group membership has been # pulled in yield addDelegate(txn, delegator, group1, True) # Passing expanded=False will return the group delegates = (yield delegatesOf(txn, delegator, True, expanded=False)) self.assertEquals(1, len(delegates)) self.assertEquals(delegates[0].uid, u"__top_group_1__") # Passing expanded=True will return not the group -- it only returns # non-groups delegates = (yield delegatesOf(txn, delegator, True, expanded=True)) self.assertEquals(0, len(delegates)) # Now refresh the group and there will be 3 delegates (contained # within 2 nested groups) # guid = "49b350c69611477b94d95516b13856ab" yield self.groupCacher.refreshGroup(txn, group1.uid) yield self.groupCacher.refreshGroup(txn, group2.uid) delegates = (yield delegatesOf(txn, delegator, True, expanded=True)) self.assertEquals( set([u"__sagen1__", u"__cdaboo1__", u"__glyph1__"]), set([d.uid for d in delegates]) ) delegators = (yield delegatedTo(txn, delegate1, True)) self.assertEquals([u"__wsanchez1__"], [d.uid for d in delegators]) # Verify we can ask for all delegated-to groups yield addDelegate(txn, delegator, group2, True) groups = (yield txn.allGroupDelegates()) self.assertEquals( set([u'__sub_group_1__', u'__top_group_1__']), set(groups) ) # Delegate to a user who is already indirectly delegated-to yield addDelegate(txn, delegator, delegate1, True) delegates = (yield delegatesOf(txn, delegator, True, expanded=True)) self.assertEquals( set([u"__sagen1__", u"__cdaboo1__", u"__glyph1__"]), set([d.uid for d in delegates]) ) # Add a member to the group; they become a delegate newSet = set() for name in (u"wsanchez1", u"cdaboo1", u"sagen1", u"glyph1", u"dre1"): record = ( yield self.directory.recordWithShortName(RecordType.user, name) ) newSet.add(record.uid) ( groupID, name, _ignore_membershipHash, _ignore_modified, _ignore_extant ) = (yield txn.groupByUID(group1.uid)) _ignore_numAdded, _ignore_numRemoved = ( yield self.groupCacher.synchronizeMembers(txn, groupID, newSet) ) delegates = (yield delegatesOf(txn, delegator, True, expanded=True)) self.assertEquals( set([u"__sagen1__", u"__cdaboo1__", u"__glyph1__", u"__dre1__"]), set([d.uid for d in delegates]) ) # Remove delegate access from the top group yield removeDelegate(txn, delegator, group1, True) delegates = (yield delegatesOf(txn, delegator, True, expanded=True)) self.assertEquals( set([u"__sagen1__", u"__cdaboo1__"]), set([d.uid for d in delegates]) ) # Remove delegate access from the sub group yield removeDelegate(txn, delegator, group2, True) delegates = (yield delegatesOf(txn, delegator, True, expanded=True)) self.assertEquals( set([u"__sagen1__"]), set([d.uid for d in delegates]) ) yield txn.commit() @inlineCallbacks def test_noDuplication(self): """ Make sure addDelegate( ) is idempotent """ delegator = yield self.directory.recordWithUID(u"__wsanchez1__") # Delegate users: delegate1 = yield self.directory.recordWithUID(u"__sagen1__") txn = self.store.newTransaction(label="test_noDuplication") yield addDelegate(txn, delegator, delegate1, True) yield txn.commit() txn = self.store.newTransaction(label="test_noDuplication") yield addDelegate(txn, delegator, delegate1, True) yield txn.commit() txn = self.store.newTransaction(label="test_noDuplication") results = ( yield txn._selectDelegatesQuery.on( txn, delegator=delegator.uid.encode("utf-8"), readWrite=1 ) ) yield txn.commit() self.assertEquals([["__sagen1__"]], results) # Delegate groups: group1 = yield self.directory.recordWithUID(u"__top_group_1__") txn = self.store.newTransaction(label="test_noDuplication") yield addDelegate(txn, delegator, group1, True) yield txn.commit() txn = self.store.newTransaction(label="test_noDuplication") yield addDelegate(txn, delegator, group1, True) yield txn.commit() txn = self.store.newTransaction(label="test_noDuplication") results = ( yield txn._selectDelegateGroupsQuery.on( txn, delegator=delegator.uid.encode("utf-8"), readWrite=1 ) ) yield txn.commit() self.assertEquals([["__top_group_1__"]], results)
def test_group_member_removal_refresh_slow(self): """ Test that the sharee list is still valid when a member is removed from a group, but sharee reconciliation has not yet occurred. """ @inlineCallbacks def expandedMembers(self, records=None, seen=None): if self.uid == "group02": returnValue(frozenset()) else: returnValue((yield unpatchedExpandedMembers(self, records, seen))) unpatchedExpandedMembers = CalendarDirectoryRecordMixin.expandedMembers # Prevent sharee reconciliation def _noop(self): return succeed(None) self.patch(GroupShareeReconciliationWork, "doWork", _noop) # 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(), "group02") 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("group02", _BIND_MODE_READ) self.assertEqual(len(shareeViews), 3) calendar = yield self.calendarUnderTest(home="user01", name="calendar") invites = yield calendar.sharingInvites() self.assertEqual(len(invites), 3) 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, ]) groupsToRefresh = yield groupCacher.groupsToRefresh(self.transactionUnderTest()) self.assertEqual(len(groupsToRefresh), 1) # 0 group members self.patch(CalendarDirectoryRecordMixin, "expandedMembers", expandedMembers) wps = yield groupCacher.refreshGroup(self.transactionUnderTest(), "group02") 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), 3) 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("group02") noinvites = yield calendar.sharingInvites() self.assertEqual(len(noinvites), 3)
class DynamicGroupTest(StoreTestCase): @inlineCallbacks def setUp(self): yield super(DynamicGroupTest, self).setUp() self.directory = CalendarInMemoryDirectoryService(None) self.store.setDirectoryService(self.directory) self.groupCacher = GroupCacher(self.directory) self.numUsers = 100 # Add users records = [] fieldName = self.directory.fieldName for i in xrange(self.numUsers): records.append( TestRecord( self.directory, { fieldName.uid: u"foo{ctr:05d}".format(ctr=i), fieldName.shortNames: (u"foo{ctr:05d}".format(ctr=i), ), fieldName.fullNames: (u"foo{ctr:05d}".format(ctr=i), ), fieldName.recordType: RecordType.user, })) # Add two groups for uid in ( u"testgroup", u"emptygroup", ): records.append( TestRecord(self.directory, { fieldName.uid: uid, fieldName.recordType: RecordType.group, })) yield self.directory.updateRecords(records, create=True) # add members to test group group = yield self.directory.recordWithUID(u"testgroup") members = yield self.directory.recordsWithRecordType(RecordType.user) yield group.setMembers(members) def doWork(self): self.transaction._groupCacher = groupCacher return unpatchedDoWork(self) groupCacher = self.groupCacher unpatchedDoWork = GroupRefreshWork.doWork self.patch(GroupRefreshWork, "doWork", doWork) config.AutomaticPurging.Enabled = True @inlineCallbacks def test_extant(self): """ Verify that once a group is removed from the directory, the next call to refreshGroup() will set the "extent" to False. Add the group back to the directory and "extent" becomes True. """ store = self.storeUnderTest() for uid in ( u"testgroup", u"emptygroup", ): txn = store.newTransaction() yield self.groupCacher.refreshGroup(txn, uid) group = yield txn.groupByUID(uid) yield txn.commit() self.assertTrue(group.extant) # Remove the group yield self.directory.removeRecords([uid]) txn = store.newTransaction() yield self.groupCacher.refreshGroup(txn, uid) group = (yield txn.groupByUID(uid)) yield txn.commit() # Extant = False self.assertFalse(group.extant) # The list of members stored in the DB for this group is now empty txn = store.newTransaction() members = yield txn.groupMemberUIDs(group.groupID) yield txn.commit() self.assertEquals(members, set()) # Add the group back into the directory fieldName = self.directory.fieldName yield self.directory.updateRecords( (TestRecord(self.directory, { fieldName.uid: uid, fieldName.recordType: RecordType.group, }), ), create=True) if uid == u"testgroup": group = yield self.directory.recordWithUID(uid) members = yield self.directory.recordsWithRecordType( RecordType.user) yield group.setMembers(members) txn = store.newTransaction() yield self.groupCacher.refreshGroup(txn, uid) group = (yield txn.groupByUID(uid)) yield txn.commit() # Extant = True self.assertTrue(group.extant) # The list of members stored in the DB for this group has 100 users txn = store.newTransaction() members = yield txn.groupMemberUIDs(group.groupID) yield txn.commit() self.assertEquals(len(members), 100 if uid == u"testgroup" else 0) @inlineCallbacks def test_update_delete_unused(self): """ Verify that unused groups are deleted from group cache """ store = self.storeUnderTest() # unused group deleted for uid in ( u"testgroup", u"emptygroup", ): txn = store.newTransaction() yield self.groupCacher.refreshGroup(txn, uid) group = yield txn.groupByUID(uid, create=False) yield txn.commit() self.assertNotEqual(group, None) txn = store.newTransaction() yield self.groupCacher.update(txn) group = yield txn.groupByUID(uid, create=False) yield txn.commit() self.assertEqual(group, None) # delegate groups not deleted for uid in ( u"testgroup", u"emptygroup", ): txn = store.newTransaction() group = yield txn.groupByUID(uid) yield txn.addDelegateGroup(delegator=u"sagen", delegateGroupID=group.groupID, readWrite=True) yield txn.commit() self.assertNotEqual(group, None) txn = store.newTransaction() yield self.groupCacher.update(txn) yield txn.commit() yield JobItem.waitEmpty(store.newTransaction, reactor, 60) txn = store.newTransaction() group = yield txn.groupByUID(uid, create=False) yield txn.commit() self.assertNotEqual(group, None) # delegate group is deleted. unused group is deleted txn = store.newTransaction() testGroup = yield txn.groupByUID(u"testgroup", create=False) yield txn.removeDelegateGroup(delegator=u"sagen", delegateGroupID=testGroup.groupID, readWrite=True) testGroup = yield txn.groupByUID(u"testgroup", create=False) emptyGroup = yield txn.groupByUID(u"emptygroup", create=False) yield txn.commit() self.assertNotEqual(testGroup, None) self.assertNotEqual(emptyGroup, None) txn = store.newTransaction() yield self.groupCacher.update(txn) yield txn.commit() yield JobItem.waitEmpty(store.newTransaction, reactor, 60) txn = store.newTransaction() testGroup = yield txn.groupByUID(u"testgroup", create=False) emptyGroup = yield txn.groupByUID(u"emptygroup", create=False) yield txn.commit() self.assertEqual(testGroup, None) self.assertNotEqual(emptyGroup, None) @inlineCallbacks def test_update_delete_old_nonextant(self): """ Verify that old missing groups are deleted from group cache """ oldGroupPurgeIntervalSeconds = config.AutomaticPurging.GroupPurgeIntervalSeconds store = self.storeUnderTest() for uid in ( u"testgroup", u"emptygroup", ): config.AutomaticPurging.GroupPurgeIntervalSeconds = oldGroupPurgeIntervalSeconds txn = store.newTransaction() group = yield txn.groupByUID(uid) yield txn.addDelegateGroup(delegator=u"sagen", delegateGroupID=group.groupID, readWrite=True) group = yield txn.groupByUID(uid, create=False) yield txn.commit() self.assertNotEqual(group, None) self.assertTrue(group.extant) # Remove the group, still cached yield self.directory.removeRecords([uid]) txn = store.newTransaction() yield self.groupCacher.update(txn) group = yield txn.groupByUID(uid, create=False) yield txn.commit() yield JobItem.waitEmpty(store.newTransaction, reactor, 60) txn = store.newTransaction() group = yield txn.groupByUID(uid, create=False) yield txn.commit() self.assertNotEqual(group, None) self.assertFalse(group.extant) # delete the group config.AutomaticPurging.GroupPurgeIntervalSeconds = "0.0" txn = store.newTransaction() yield self.groupCacher.update(txn) group = yield txn.groupByUID(uid, create=False) yield txn.commit() self.assertEqual(group, None)
def setUp(self): yield super(DelegationTest, self).setUp() self.store = self.storeUnderTest() self.groupCacher = GroupCacher(self.directory)
def test_directoryBasedDelegationChanges(self): groupCacher = GroupCacher(self.directory) delegator = yield self.directory.recordWithUID(u"__wsanchez1__") groupRecord1 = yield self.directory.recordWithUID(u"__top_group_1__") group1 = yield self.transactionUnderTest().groupByUID("__top_group_1__") groupRecord2 = yield self.directory.recordWithUID(u"__sub_group_1__") group2 = yield self.transactionUnderTest().groupByUID("__sub_group_1__") groupRecord3 = yield self.directory.recordWithUID(u"left_coast") group3 = yield self.transactionUnderTest().groupByUID("left_coast") delegate = yield self.directory.recordWithUID(u"__sagen1__") # No delegates delegates = yield Delegates.delegatesOf(self.transactionUnderTest(), delegator, False) self.assertEquals(len(delegates), 0) # No delegators to this group delegators = yield self.transactionUnderTest().delegatorsToGroup(group1.groupID, False) self.assertEquals(len(delegators), 0) # User is not a delegate delegators = yield Delegates.delegatedTo(self.transactionUnderTest(), delegate, True) self.assertEquals(len(delegators), 0) # Apply an external read-only assignment yield groupCacher.applyExternalAssignments( self.transactionUnderTest(), delegator.uid, groupRecord1.uid, None ) # Now there is a read-only delegate delegates = yield Delegates.delegatesOf(self.transactionUnderTest(), delegator, False) self.assertEquals(len(delegates), 1) # Now this group is read-only delegated to delegators = yield self.transactionUnderTest().delegatorsToGroup(group1.groupID, False) self.assertEquals(len(delegators), 1) # Apply an external read-write assignment yield groupCacher.applyExternalAssignments( self.transactionUnderTest(), delegator.uid, groupRecord1.uid, groupRecord2.uid ) # Now there are read-only and read-write delegates delegates = yield Delegates.delegatesOf(self.transactionUnderTest(), delegator, False) self.assertEquals(len(delegates), 1) self.assertEquals(delegates[0].uid, "__top_group_1__") delegates = yield Delegates.delegatesOf(self.transactionUnderTest(), delegator, True) self.assertEquals(len(delegates), 1) self.assertEquals(delegates[0].uid, "__sub_group_1__") # Now both groups are delegated to delegators = yield self.transactionUnderTest().delegatorsToGroup(group1.groupID, False) self.assertEquals(len(delegators), 1) delegators = yield self.transactionUnderTest().delegatorsToGroup(group2.groupID, True) self.assertEquals(len(delegators), 1) # User is now a delegate (cache must have been invalidated properly) delegators = yield Delegates.delegatedTo(self.transactionUnderTest(), delegate, True) self.assertEquals(len(delegators), 1) # Change read-write assignment yield groupCacher.applyExternalAssignments( self.transactionUnderTest(), delegator.uid, groupRecord1.uid, groupRecord3.uid ) # Now this group is not delegated to delegators = yield self.transactionUnderTest().delegatorsToGroup(group2.groupID, True) self.assertEquals(len(delegators), 0) # ..but this group is delegated to delegators = yield self.transactionUnderTest().delegatorsToGroup(group3.groupID, True) self.assertEquals(len(delegators), 1) # Remove external read-write assignment yield groupCacher.applyExternalAssignments( self.transactionUnderTest(), delegator.uid, groupRecord1.uid, None ) # Now there is only a read-only delegate delegates = yield Delegates.delegatesOf(self.transactionUnderTest(), delegator, False) self.assertEquals(len(delegates), 1) self.assertEquals(delegates[0].uid, "__top_group_1__") delegates = yield Delegates.delegatesOf(self.transactionUnderTest(), delegator, True) self.assertEquals(len(delegates), 0) # Now this group is read-only delegated to delegators = yield self.transactionUnderTest().delegatorsToGroup(group1.groupID, False) self.assertEquals(len(delegators), 1) # Now this group is not delegated to delegators = yield self.transactionUnderTest().delegatorsToGroup(group3.groupID, True) self.assertEquals(len(delegators), 0) # User is not a delegate anymore (cache must have been invalidated properly) delegators = yield Delegates.delegatedTo(self.transactionUnderTest(), delegate, True) self.assertEquals(len(delegators), 0) # Remove external assignments altogether yield groupCacher.applyExternalAssignments( self.transactionUnderTest(), delegator.uid, None, None ) # Now there are no delegates delegates = yield Delegates.delegatesOf(self.transactionUnderTest(), delegator, False) self.assertEquals(len(delegates), 0) # No groups are delegated to delegators = yield self.transactionUnderTest().delegatorsToGroup(group1.groupID, False) self.assertEquals(len(delegators), 0) delegators = yield self.transactionUnderTest().delegatorsToGroup(group2.groupID, True) self.assertEquals(len(delegators), 0) delegators = yield self.transactionUnderTest().delegatorsToGroup(group3.groupID, True) self.assertEquals(len(delegators), 0)
class GroupCacherTest(StoreTestCase): @inlineCallbacks def setUp(self): yield super(GroupCacherTest, self).setUp() self.groupCacher = GroupCacher(self.directory) @inlineCallbacks def test_multipleCalls(self): """ Ensure multiple calls to groupByUID() don't raise an exception """ store = self.storeUnderTest() txn = store.newTransaction() record = yield self.directory.recordWithUID(u"__top_group_1__") yield txn.groupByUID(record.uid) yield txn.groupByUID(record.uid) yield txn.commit() @inlineCallbacks def test_refreshGroup(self): """ Verify refreshGroup() adds a group to the Groups table with the expected membership hash value and members """ store = self.storeUnderTest() txn = store.newTransaction() record = yield self.directory.recordWithUID(u"__top_group_1__") yield self.groupCacher.refreshGroup(txn, record.uid) group = (yield txn.groupByUID(record.uid)) self.assertEquals(group.extant, True) self.assertEquals(group.membershipHash, "553eb54e3bbb26582198ee04541dbee4") group = yield txn.groupByID(group.groupID) self.assertEquals(group.groupUID, record.uid) self.assertEquals(group.name, u"Top Group 1") self.assertEquals(group.membershipHash, "553eb54e3bbb26582198ee04541dbee4") self.assertEquals(group.extant, True) members = (yield txn.groupMemberUIDs(group.groupID)) self.assertEquals( set([u'__cdaboo1__', u'__glyph1__', u'__sagen1__', u'__wsanchez1__']), members ) records = (yield self.groupCacher.cachedMembers(txn, group.groupID)) self.assertEquals( set([r.uid for r in records]), set([u'__cdaboo1__', u'__glyph1__', u'__sagen1__', u'__wsanchez1__']) ) # sagen is in the top group, even though it's actually one level # removed record = yield self.directory.recordWithUID(u"__sagen1__") groups = (yield self.groupCacher.cachedGroupsFor(txn, record.uid)) self.assertEquals(set([u"__top_group_1__"]), groups) yield txn.commit() @inlineCallbacks def test_synchronizeMembers(self): """ After loading in a group via refreshGroup(), pass new member sets to synchronizeMembers() and verify members are added and removed as expected """ store = self.storeUnderTest() txn = store.newTransaction() # Refresh the group so it's assigned a group_id uid = u"__top_group_1__" yield self.groupCacher.refreshGroup(txn, uid) group = yield txn.groupByUID(uid) # Remove two members, and add one member newSet = set() for name in (u"wsanchez1", u"cdaboo1", u"dre1"): record = ( yield self.directory.recordWithShortName( RecordType.user, name ) ) newSet.add(record.uid) added, removed = ( yield self.groupCacher.synchronizeMembers( txn, group.groupID, newSet ) ) self.assertEquals(added, set(["__dre1__", ])) self.assertEquals(removed, set(["__glyph1__", "__sagen1__", ])) records = (yield self.groupCacher.cachedMembers(txn, group.groupID)) self.assertEquals( set([r.shortNames[0] for r in records]), set(["wsanchez1", "cdaboo1", "dre1"]) ) # Remove all members added, removed = ( yield self.groupCacher.synchronizeMembers(txn, group.groupID, set()) ) self.assertEquals(added, set()) self.assertEquals(removed, set(["__wsanchez1__", "__cdaboo1__", "__dre1__", ])) records = (yield self.groupCacher.cachedMembers(txn, group.groupID)) self.assertEquals(len(records), 0) yield txn.commit() @inlineCallbacks def test_groupByID(self): store = self.storeUnderTest() txn = store.newTransaction() # Non-existent groupID yield self.failUnlessFailure(txn.groupByID(42), NotFoundError) uid = u"__top_group_1__" hash = "553eb54e3bbb26582198ee04541dbee4" yield self.groupCacher.refreshGroup(txn, uid) group = yield txn.groupByUID(uid) group = yield txn.groupByID(group.groupID) self.assertEqual(group.groupUID, uid) self.assertEqual(group.name, u"Top Group 1") self.assertEqual(group.membershipHash, hash) self.assertEqual(group.extant, True) yield txn.commit() @inlineCallbacks def test_externalAssignments(self): store = self.storeUnderTest() txn = store.newTransaction() oldExternalAssignments = (yield txn.externalDelegates()) self.assertEquals(oldExternalAssignments, {}) newAssignments = { u"__wsanchez1__": (None, u"__top_group_1__") } yield self.groupCacher.scheduleExternalAssignments( txn, newAssignments, immediately=True ) oldExternalAssignments = (yield txn.externalDelegates()) self.assertEquals( oldExternalAssignments, { u"__wsanchez1__": ( None, u"__top_group_1__" ) } ) newAssignments = { u"__cdaboo1__": ( u"__sub_group_1__", None ), u"__wsanchez1__": ( u"__sub_group_1__", u"__top_group_1__" ), } yield self.groupCacher.scheduleExternalAssignments( txn, newAssignments, immediately=True ) oldExternalAssignments = (yield txn.externalDelegates()) self.assertEquals( oldExternalAssignments, { u"__wsanchez1__": ( u"__sub_group_1__", u"__top_group_1__" ), u"__cdaboo1__": ( u"__sub_group_1__", None ) } ) allGroupDelegates = (yield txn.allGroupDelegates()) self.assertEquals( allGroupDelegates, set( [ u"__top_group_1__", u"__sub_group_1__" ] ) ) # Fault in the read-only group yield self.groupCacher.refreshGroup(txn, u"__sub_group_1__") # Wilfredo should have Sagen and Daboo as read-only delegates delegates = (yield txn.delegates( u"__wsanchez1__", False, expanded=True) ) self.assertEquals( delegates, set( [ u"__sagen1__", u"__cdaboo1__" ] ) ) # Fault in the read-write group yield self.groupCacher.refreshGroup(txn, u"__top_group_1__") # Wilfredo should have 4 users as read-write delegates delegates = (yield txn.delegates( u"__wsanchez1__", True, expanded=True) ) self.assertEquals( delegates, set( [ u"__sagen1__", u"__cdaboo1__", u"__glyph1__" ] ) ) # # Now, remove some external assignments # newAssignments = { u"__wsanchez1__": ( u"__sub_group_1__", None ), } yield self.groupCacher.scheduleExternalAssignments( txn, newAssignments, immediately=True ) oldExternalAssignments = (yield txn.externalDelegates()) self.assertEquals( oldExternalAssignments, { u"__wsanchez1__": ( u"__sub_group_1__", None ), } ) allGroupDelegates = (yield txn.allGroupDelegates()) self.assertEquals( allGroupDelegates, set( [ u"__sub_group_1__" ] ) ) # Wilfredo should have Sagen and Daboo as read-only delegates delegates = (yield txn.delegates( u"__wsanchez1__", False, expanded=True) ) self.assertEquals( delegates, set( [ u"__sagen1__", u"__cdaboo1__" ] ) ) # Wilfredo should have no read-write delegates delegates = (yield txn.delegates( u"__wsanchez1__", True, expanded=True) ) self.assertEquals( delegates, set([]) ) # Only 1 group as delegate now: allGroupDelegates = (yield txn.allGroupDelegates()) self.assertEquals( allGroupDelegates, set( [ u"__sub_group_1__" ] ) ) # # Say somebody messed up and stuck a non-existent group UID in # as a delegate # newAssignments = { u"__wsanchez1__": ( u"__sub_group_1__", u"__non_existent_group__", ), } yield self.groupCacher.scheduleExternalAssignments( txn, newAssignments, immediately=True ) oldExternalAssignments = (yield txn.externalDelegates()) self.assertEquals( oldExternalAssignments, { u"__wsanchez1__": ( u"__sub_group_1__", None # <--- (not __non_existent_group__) ), } ) yield txn.commit() def test_diffAssignments(self): """ Ensure external proxy assignment diffing works """ self.assertEquals( ( # changed [], # removed [], ), diffAssignments( # old {}, # new {} ) ) self.assertEquals( ( # changed [], # removed [], ), diffAssignments( # old {"B": ("1", "2")}, # new {"B": ("1", "2")}, ) ) self.assertEquals( map(set, ( # changed [("A", ("1", "2")), ("B", ("3", "4"))], # removed [], )), map(set, diffAssignments( # old {}, # new {"A": ("1", "2"), "B": ("3", "4")} )) ) self.assertEquals( map(set, ( # changed [], # removed ["A", "B"], )), map(set, diffAssignments( # old {"A": ("1", "2"), "B": ("3", "4")}, # new {}, )) ) self.assertEquals( map(set, ( # changed [('C', ('4', '5')), ('D', ('7', '8'))], # removed ["B"], )), map(set, diffAssignments( # old {"A": ("1", "2"), "B": ("3", "4"), "C": ("5", "6")}, # new {"D": ("7", "8"), "C": ("4", "5"), "A": ("1", "2")}, )) ) @inlineCallbacks def test_recursiveGroup(self): """ Verify refreshGroup() adds a group to the Groups table with the expected membership hash value and members """ store = self.storeUnderTest() txn = store.newTransaction() record = yield self.directory.recordWithUID(u"recursive1_coasts") members = yield record.expandedMembers() self.assertEquals( set([r.uid for r in members]), set([u'6423F94A-6B76-4A3A-815B-D52CFD77935D', u'5A985493-EE2C-4665-94CF-4DFEA3A89500']) ) yield txn.commit() @inlineCallbacks def test_groupChangeCacheNotification(self): """ Verify refreshGroup() triggers a cache notification for the group and all members that are added or removed """ class TestNotifier(object): changedTokens = [] def changed(self, token): self.changedTokens.append(token) self.groupCacher.cacheNotifier = TestNotifier() # No change yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__top_group_1__") yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__sub_group_1__") yield self.commit() self.assertEqual(TestNotifier.changedTokens, []) # Add member to group record = yield self.directory.recordWithUID(u"__top_group_1__") addrecord = yield self.directory.recordWithUID(u"__dre1__") yield record.addMembers([addrecord, ]) yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__top_group_1__") yield self.commit() self.assertEqual(TestNotifier.changedTokens, [ "__top_group_1__", "__dre1__", ]) TestNotifier.changedTokens = [] # Remove member from group record = yield self.directory.recordWithUID(u"__top_group_1__") addrecord = yield self.directory.recordWithUID(u"__dre1__") yield record.removeMembers([addrecord, ]) yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__top_group_1__") yield self.commit() self.assertEqual(TestNotifier.changedTokens, [ "__top_group_1__", "__dre1__", ]) TestNotifier.changedTokens = [] # Add member to sub-group record = yield self.directory.recordWithUID(u"__sub_group_1__") addrecord = yield self.directory.recordWithUID(u"__dre1__") yield record.addMembers([addrecord, ]) yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__top_group_1__") yield self.commit() self.assertEqual(TestNotifier.changedTokens, [ "__top_group_1__", "__dre1__", ]) TestNotifier.changedTokens = [] yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__sub_group_1__") yield self.commit() self.assertEqual(TestNotifier.changedTokens, [ "__sub_group_1__", "__dre1__", ]) TestNotifier.changedTokens = [] # Remove member from sub-group record = yield self.directory.recordWithUID(u"__sub_group_1__") addrecord = yield self.directory.recordWithUID(u"__dre1__") yield record.removeMembers([addrecord, ]) yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__top_group_1__") yield self.commit() self.assertEqual(TestNotifier.changedTokens, [ "__top_group_1__", "__dre1__", ]) TestNotifier.changedTokens = [] yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__sub_group_1__") yield self.commit() self.assertEqual(TestNotifier.changedTokens, [ "__sub_group_1__", "__dre1__", ]) TestNotifier.changedTokens = [] # Remove sub-group member from group record = yield self.directory.recordWithUID(u"__top_group_1__") addrecord = yield self.directory.recordWithUID(u"__sub_group_1__") yield record.removeMembers([addrecord, ]) yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__top_group_1__") yield self.commit() self.assertEqual(set(TestNotifier.changedTokens), set([ "__top_group_1__", "__sagen1__", "__cdaboo1__", ])) TestNotifier.changedTokens = [] # Add sub-group member to group record = yield self.directory.recordWithUID(u"__top_group_1__") addrecord = yield self.directory.recordWithUID(u"__sub_group_1__") yield record.addMembers([addrecord, ]) yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__top_group_1__") yield self.commit() self.assertEqual(set(TestNotifier.changedTokens), set([ "__top_group_1__", "__sagen1__", "__cdaboo1__", ])) TestNotifier.changedTokens = []