class GroupCacherTest(StoreTestCase): @inlineCallbacks def setUp(self): yield super(GroupCacherTest, self).setUp() self.groupCacher = GroupCacher(self.directory) @inlineCallbacks def test_multipleCalls(self): """ Ensure multiple calls to groupByUID() don't raise an exception """ store = self.storeUnderTest() txn = store.newTransaction() record = yield self.directory.recordWithUID(u"__top_group_1__") yield txn.groupByUID(record.uid) yield txn.groupByUID(record.uid) yield txn.commit() @inlineCallbacks def test_refreshGroup(self): """ Verify refreshGroup() adds a group to the Groups table with the expected membership hash value and members """ store = self.storeUnderTest() txn = store.newTransaction() record = yield self.directory.recordWithUID(u"__top_group_1__") yield self.groupCacher.refreshGroup(txn, record.uid) group = (yield txn.groupByUID(record.uid)) self.assertEquals(group.extant, True) self.assertEquals(group.membershipHash, "553eb54e3bbb26582198ee04541dbee4") group = yield txn.groupByID(group.groupID) self.assertEquals(group.groupUID, record.uid) self.assertEquals(group.name, u"Top Group 1") self.assertEquals(group.membershipHash, "553eb54e3bbb26582198ee04541dbee4") self.assertEquals(group.extant, True) members = (yield txn.groupMemberUIDs(group.groupID)) self.assertEquals( set([ u'__cdaboo1__', u'__glyph1__', u'__sagen1__', u'__wsanchez1__' ]), members) records = (yield self.groupCacher.cachedMembers(txn, group.groupID)) self.assertEquals( set([r.uid for r in records]), set([ u'__cdaboo1__', u'__glyph1__', u'__sagen1__', u'__wsanchez1__' ])) # sagen is in the top group, even though it's actually one level # removed record = yield self.directory.recordWithUID(u"__sagen1__") groups = (yield self.groupCacher.cachedGroupsFor(txn, record.uid)) self.assertEquals(set([u"__top_group_1__"]), groups) yield txn.commit() @inlineCallbacks def test_synchronizeMembers(self): """ After loading in a group via refreshGroup(), pass new member sets to synchronizeMembers() and verify members are added and removed as expected """ store = self.storeUnderTest() txn = store.newTransaction() # Refresh the group so it's assigned a group_id uid = u"__top_group_1__" yield self.groupCacher.refreshGroup(txn, uid) group = yield txn.groupByUID(uid) # Remove two members, and add one member newSet = set() for name in (u"wsanchez1", u"cdaboo1", u"dre1"): record = (yield self.directory.recordWithShortName( RecordType.user, name)) newSet.add(record.uid) added, removed = (yield self.groupCacher.synchronizeMembers( txn, group.groupID, newSet)) self.assertEquals(added, set([ "__dre1__", ])) self.assertEquals(removed, set([ "__glyph1__", "__sagen1__", ])) records = (yield self.groupCacher.cachedMembers(txn, group.groupID)) self.assertEquals(set([r.shortNames[0] for r in records]), set(["wsanchez1", "cdaboo1", "dre1"])) # Remove all members added, removed = (yield self.groupCacher.synchronizeMembers( txn, group.groupID, set())) self.assertEquals(added, set()) self.assertEquals(removed, set([ "__wsanchez1__", "__cdaboo1__", "__dre1__", ])) records = (yield self.groupCacher.cachedMembers(txn, group.groupID)) self.assertEquals(len(records), 0) yield txn.commit() @inlineCallbacks def test_groupByID(self): store = self.storeUnderTest() txn = store.newTransaction() # Non-existent groupID yield self.failUnlessFailure(txn.groupByID(42), NotFoundError) uid = u"__top_group_1__" hash = "553eb54e3bbb26582198ee04541dbee4" yield self.groupCacher.refreshGroup(txn, uid) group = yield txn.groupByUID(uid) group = yield txn.groupByID(group.groupID) self.assertEqual(group.groupUID, uid) self.assertEqual(group.name, u"Top Group 1") self.assertEqual(group.membershipHash, hash) self.assertEqual(group.extant, True) yield txn.commit() @inlineCallbacks def test_externalAssignments(self): store = self.storeUnderTest() txn = store.newTransaction() oldExternalAssignments = (yield txn.externalDelegates()) self.assertEquals(oldExternalAssignments, {}) newAssignments = {u"__wsanchez1__": (None, u"__top_group_1__")} yield self.groupCacher.scheduleExternalAssignments(txn, newAssignments, immediately=True) oldExternalAssignments = (yield txn.externalDelegates()) self.assertEquals(oldExternalAssignments, {u"__wsanchez1__": (None, u"__top_group_1__")}) newAssignments = { u"__cdaboo1__": (u"__sub_group_1__", None), u"__wsanchez1__": (u"__sub_group_1__", u"__top_group_1__"), } yield self.groupCacher.scheduleExternalAssignments(txn, newAssignments, immediately=True) oldExternalAssignments = (yield txn.externalDelegates()) self.assertEquals( oldExternalAssignments, { u"__wsanchez1__": (u"__sub_group_1__", u"__top_group_1__"), u"__cdaboo1__": (u"__sub_group_1__", None) }) allGroupDelegates = (yield txn.allGroupDelegates()) self.assertEquals(allGroupDelegates, set([u"__top_group_1__", u"__sub_group_1__"])) # Fault in the read-only group yield self.groupCacher.refreshGroup(txn, u"__sub_group_1__") # Wilfredo should have Sagen and Daboo as read-only delegates delegates = (yield txn.delegates(u"__wsanchez1__", False, expanded=True)) self.assertEquals(delegates, set([u"__sagen1__", u"__cdaboo1__"])) # Fault in the read-write group yield self.groupCacher.refreshGroup(txn, u"__top_group_1__") # Wilfredo should have 4 users as read-write delegates delegates = (yield txn.delegates(u"__wsanchez1__", True, expanded=True)) self.assertEquals(delegates, set([u"__sagen1__", u"__cdaboo1__", u"__glyph1__"])) # # Now, remove some external assignments # newAssignments = { u"__wsanchez1__": (u"__sub_group_1__", None), } yield self.groupCacher.scheduleExternalAssignments(txn, newAssignments, immediately=True) oldExternalAssignments = (yield txn.externalDelegates()) self.assertEquals(oldExternalAssignments, { u"__wsanchez1__": (u"__sub_group_1__", None), }) allGroupDelegates = (yield txn.allGroupDelegates()) self.assertEquals(allGroupDelegates, set([u"__sub_group_1__"])) # Wilfredo should have Sagen and Daboo as read-only delegates delegates = (yield txn.delegates(u"__wsanchez1__", False, expanded=True)) self.assertEquals(delegates, set([u"__sagen1__", u"__cdaboo1__"])) # Wilfredo should have no read-write delegates delegates = (yield txn.delegates(u"__wsanchez1__", True, expanded=True)) self.assertEquals(delegates, set([])) # Only 1 group as delegate now: allGroupDelegates = (yield txn.allGroupDelegates()) self.assertEquals(allGroupDelegates, set([u"__sub_group_1__"])) # # Say somebody messed up and stuck a non-existent group UID in # as a delegate # newAssignments = { u"__wsanchez1__": ( u"__sub_group_1__", u"__non_existent_group__", ), } yield self.groupCacher.scheduleExternalAssignments(txn, newAssignments, immediately=True) oldExternalAssignments = (yield txn.externalDelegates()) self.assertEquals( oldExternalAssignments, { u"__wsanchez1__": ( u"__sub_group_1__", None # <--- (not __non_existent_group__) ), }) yield txn.commit() def test_diffAssignments(self): """ Ensure external proxy assignment diffing works """ self.assertEquals( ( # changed [], # removed [], ), diffAssignments( # old {}, # new {})) self.assertEquals( ( # changed [], # removed [], ), diffAssignments( # old {"B": ("1", "2")}, # new {"B": ("1", "2")}, )) self.assertEquals( map( set, ( # changed [("A", ("1", "2")), ("B", ("3", "4"))], # removed [], )), map( set, diffAssignments( # old {}, # new { "A": ("1", "2"), "B": ("3", "4") }))) self.assertEquals( map( set, ( # changed [], # removed ["A", "B"], )), map( set, diffAssignments( # old { "A": ("1", "2"), "B": ("3", "4") }, # new {}, ))) self.assertEquals( map( set, ( # changed [('C', ('4', '5')), ('D', ('7', '8'))], # removed ["B"], )), map( set, diffAssignments( # old { "A": ("1", "2"), "B": ("3", "4"), "C": ("5", "6") }, # new { "D": ("7", "8"), "C": ("4", "5"), "A": ("1", "2") }, ))) @inlineCallbacks def test_recursiveGroup(self): """ Verify refreshGroup() adds a group to the Groups table with the expected membership hash value and members """ store = self.storeUnderTest() txn = store.newTransaction() record = yield self.directory.recordWithUID(u"recursive1_coasts") members = yield record.expandedMembers() self.assertEquals( set([r.uid for r in members]), set([ u'6423F94A-6B76-4A3A-815B-D52CFD77935D', u'5A985493-EE2C-4665-94CF-4DFEA3A89500' ])) yield txn.commit() @inlineCallbacks def test_groupChangeCacheNotificationRefreshGroup(self): """ Verify refreshGroup() triggers a cache notification for the group and all members that are added or removed """ class TestNotifier(object): changedTokens = [] def changed(self, token): self.changedTokens.append(token) self.groupCacher.cacheNotifier = TestNotifier() # No change yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__top_group_1__") yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__sub_group_1__") yield self.commit() self.assertEqual(TestNotifier.changedTokens, []) # Add member to group record = yield self.directory.recordWithUID(u"__top_group_1__") addrecord = yield self.directory.recordWithUID(u"__dre1__") yield record.addMembers([ addrecord, ]) yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__top_group_1__") yield self.commit() self.assertEqual(TestNotifier.changedTokens, [ "__top_group_1__", "__dre1__", ]) TestNotifier.changedTokens = [] # Remove member from group record = yield self.directory.recordWithUID(u"__top_group_1__") addrecord = yield self.directory.recordWithUID(u"__dre1__") yield record.removeMembers([ addrecord, ]) yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__top_group_1__") yield self.commit() self.assertEqual(TestNotifier.changedTokens, [ "__top_group_1__", "__dre1__", ]) TestNotifier.changedTokens = [] # Add member to sub-group record = yield self.directory.recordWithUID(u"__sub_group_1__") addrecord = yield self.directory.recordWithUID(u"__dre1__") yield record.addMembers([ addrecord, ]) yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__top_group_1__") yield self.commit() self.assertEqual(TestNotifier.changedTokens, [ "__top_group_1__", "__dre1__", ]) TestNotifier.changedTokens = [] yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__sub_group_1__") yield self.commit() self.assertEqual(TestNotifier.changedTokens, [ "__sub_group_1__", "__dre1__", ]) TestNotifier.changedTokens = [] # Remove member from sub-group record = yield self.directory.recordWithUID(u"__sub_group_1__") addrecord = yield self.directory.recordWithUID(u"__dre1__") yield record.removeMembers([ addrecord, ]) yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__top_group_1__") yield self.commit() self.assertEqual(TestNotifier.changedTokens, [ "__top_group_1__", "__dre1__", ]) TestNotifier.changedTokens = [] yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__sub_group_1__") yield self.commit() self.assertEqual(TestNotifier.changedTokens, [ "__sub_group_1__", "__dre1__", ]) TestNotifier.changedTokens = [] # Remove sub-group member from group record = yield self.directory.recordWithUID(u"__top_group_1__") addrecord = yield self.directory.recordWithUID(u"__sub_group_1__") yield record.removeMembers([ addrecord, ]) yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__top_group_1__") yield self.commit() self.assertEqual( set(TestNotifier.changedTokens), set([ "__top_group_1__", "__sagen1__", "__cdaboo1__", ])) TestNotifier.changedTokens = [] # Add sub-group member to group record = yield self.directory.recordWithUID(u"__top_group_1__") addrecord = yield self.directory.recordWithUID(u"__sub_group_1__") yield record.addMembers([ addrecord, ]) yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__top_group_1__") yield self.commit() self.assertEqual( set(TestNotifier.changedTokens), set([ "__top_group_1__", "__sagen1__", "__cdaboo1__", ])) TestNotifier.changedTokens = [] @inlineCallbacks def test_groupChangeCacheNotificationApplyExternalAssignments(self): """ Verify applyExternalAssignments() triggers a cache notification for the delegator and delegates """ class TestNotifier(object): changedTokens = [] def changed(self, token): self.changedTokens.append(token) self.groupCacher.cacheNotifier = TestNotifier() yield self.groupCacher.applyExternalAssignments( self.transactionUnderTest(), "__dre1__", None, None) yield self.commit() self.assertEqual(TestNotifier.changedTokens, ["__dre1__"]) TestNotifier.changedTokens = [] yield self.groupCacher.applyExternalAssignments( self.transactionUnderTest(), "__dre1__", "__top_group_1__", "__sub_group_1__") yield self.commit() self.assertEqual(TestNotifier.changedTokens, ["__dre1__", "__top_group_1__", "__sub_group_1__"]) TestNotifier.changedTokens = []
class 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)
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])
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])
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 = []