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]))
Exemple #2
0
    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 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()
Exemple #4
0
    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
Exemple #5
0
    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())
    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
Exemple #7
0
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])
        )
Exemple #8
0
    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)
Exemple #9
0
    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)
Exemple #10
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.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)
Exemple #11
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)
Exemple #12
0
    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)
Exemple #13
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)
Exemple #18
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)
Exemple #19
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")
                },
            ))
Exemple #20
0
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)
Exemple #21
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 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)
Exemple #26
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_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 = []
Exemple #27
0
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)
Exemple #29
0
 def setUp(self):
     yield super(AugmentTest, self).setUp()
     self.groupCacher = GroupCacher(self.directory)
    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)
Exemple #31
0
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)
Exemple #32
0
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 setUp(self):
     yield super(DelegationTest, self).setUp()
     self.store = self.storeUnderTest()
     self.groupCacher = GroupCacher(self.directory)
Exemple #34
0
    def setUp(self):
        yield super(DelegationTest, self).setUp()
        self.store = self.storeUnderTest()
        self.groupCacher = GroupCacher(self.directory)

        yield Delegates._memcacher.flushAll()
Exemple #35
0
    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 = []