[CalendarServer-changes] [3573] CalendarServer/trunk/twistedcaldav/directory

source_changes at macosforge.org source_changes at macosforge.org
Wed Jan 7 10:15:57 PST 2009


Revision: 3573
          http://trac.macosforge.org/projects/calendarserver/changeset/3573
Author:   cdaboo at apple.com
Date:     2009-01-07 10:15:56 -0800 (Wed, 07 Jan 2009)
Log Message:
-----------
Delete entries from the delegate DB if users are no longer in the directory.

Modified Paths:
--------------
    CalendarServer/trunk/twistedcaldav/directory/calendaruserproxy.py
    CalendarServer/trunk/twistedcaldav/directory/principal.py
    CalendarServer/trunk/twistedcaldav/directory/test/test_proxyprincipaldb.py
    CalendarServer/trunk/twistedcaldav/directory/test/test_proxyprincipalmembers.py

Modified: CalendarServer/trunk/twistedcaldav/directory/calendaruserproxy.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/calendaruserproxy.py	2009-01-06 20:23:10 UTC (rev 3572)
+++ CalendarServer/trunk/twistedcaldav/directory/calendaruserproxy.py	2009-01-07 18:15:56 UTC (rev 3573)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2006-2008 Apple Inc. All rights reserved.
+# Copyright (c) 2006-2009 Apple Inc. All rights reserved.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -70,7 +70,7 @@
         return davxml.ACL(*aces)
 
     def accessControlList(self, request, inheritance=True, expanding=False, inherited_aces=None):
-        # Permissions here are fixed, and are not subject to inherritance rules, etc.
+        # Permissions here are fixed, and are not subject to inheritance rules, etc.
         return succeed(self.defaultAccessControlList())
 
 class CalendarUserProxyPrincipalResource (CalDAVComplianceMixIn, PermissionsMixIn, DAVPrincipalResource, DAVFile):
@@ -196,12 +196,15 @@
             principals.append(principal)
             yield principal.cacheNotifier.changed()
 
+        yield self.setGroupMemberSetPrincipals(principals)
+        yield self.parent.cacheNotifier.changed()
+        returnValue(True)
+
+    @inlineCallbacks
+    def setGroupMemberSetPrincipals(self, principals):
         # Map the principals to UIDs.
         uids = [p.principalUID() for p in principals]
-
         yield self._index().setGroupMembers(self.uid, uids)
-        yield self.parent.cacheNotifier.changed()
-        returnValue(True)
 
     ##
     # HTTP
@@ -314,7 +317,20 @@
         if self.hasEditableMembership():
             # Get member UIDs from database and map to principal resources
             members = yield self._index().getMembers(self.uid)
-            returnValue([p for p in [self.pcollection.principalForUID(uid) for uid in members] if p])
+            found = []
+            missing = []
+            for uid in members:
+                p = self.pcollection.principalForUID(uid)
+                if p:
+                    found.append(p)
+                else:
+                    missing.append(uid)
+
+            # Clean-up ones that are missing
+            for uid in missing:
+                yield self._index().removePrincipal(uid)
+
+            returnValue(found)
         else:
             # Fixed proxies
             if self.proxyType == "calendar-proxy-write":
@@ -357,10 +373,10 @@
     class ProxyDBMemcacher(Memcacher):
         
         def setMembers(self, guid, members):
-            return self.set("members:%s" % (guid,), str(",".join(members)))
+            return self.set("members:%s" % (str(guid),), str(",".join(members)))
 
         def setMemberships(self, guid, memberships):
-            return self.set("memberships:%s" % (guid,), str(",".join(memberships)))
+            return self.set("memberships:%s" % (str(guid),), str(",".join(memberships)))
 
         def getMembers(self, guid):
             def _value(value):
@@ -370,7 +386,7 @@
                     return None
                 else:
                     return set()
-            d = self.get("members:%s" % (guid,))
+            d = self.get("members:%s" % (str(guid),))
             d.addCallback(_value)
             return d
 
@@ -382,15 +398,15 @@
                     return None
                 else:
                     return set()
-            d = self.get("memberships:%s" % (guid,))
+            d = self.get("memberships:%s" % (str(guid),))
             d.addCallback(_value)
             return d
 
         def deleteMember(self, guid):
-            return self.delete("members:%s" % (guid,))
+            return self.delete("members:%s" % (str(guid),))
 
         def deleteMembership(self, guid):
-            return self.delete("memberships:%s" % (guid,))
+            return self.delete("memberships:%s" % (str(guid),))
 
     def __init__(self, path):
         path = os.path.join(path, CalendarUserProxyDatabase.dbFilename)
@@ -435,17 +451,46 @@
         @param principalUID: the UID of the group principal to remove.
         """
 
+        # Need to get the members before we do the delete
+        members = yield self.getMembers(principalUID)
+
         self._delete_from_db(principalUID)
         self._db_commit()
         
         # Update cache
-        members = yield self.getMembers(principalUID)
         if members:
             for member in members:
                 yield self._memcacher.deleteMembership(member)
             yield self._memcacher.deleteMember(principalUID)
 
     @inlineCallbacks
+    def removePrincipal(self, principalUID):
+        """
+        Remove a group membership record.
+
+        @param principalUID: the UID of the principal to remove.
+        """
+        
+        for suffix in ("calendar-proxy-read", "calendar-proxy-write",):
+            groupUID = "%s#%s" % (principalUID, suffix,)
+            self._delete_from_db(groupUID)
+            
+            # Update cache
+            members = yield self.getMembers(groupUID)
+            if members:
+                for member in members:
+                    yield self._memcacher.deleteMembership(member)
+                yield self._memcacher.deleteMember(groupUID)
+
+        memberships = (yield self.getMemberships(principalUID))
+        for groupUID in memberships:
+            yield self._memcacher.deleteMember(groupUID)
+            
+        self._delete_from_db_member(principalUID)
+        yield self._memcacher.deleteMembership(principalUID)
+        self._db_commit()
+
+    @inlineCallbacks
     def getMembers(self, principalUID):
         """
         Return the list of group member UIDs for the specified principal.
@@ -454,10 +499,7 @@
         """
 
         def _members():
-            members = set()
-            for row in self._db_execute("select MEMBER from GROUPS where GROUPNAME = :1", principalUID):
-                members.add(row[0])
-            return members
+            return set([row[0] for row in self._db_execute("select MEMBER from GROUPS where GROUPNAME = :1", principalUID)])
 
         # Pull from cache
         result = yield self._memcacher.getMembers(principalUID)
@@ -475,10 +517,7 @@
         """
 
         def _members():
-            members = set()
-            for row in self._db_execute("select GROUPNAME from GROUPS where MEMBER = :1", principalUID):
-                members.add(row[0])
-            return members
+            return set([row[0] for row in self._db_execute("select GROUPNAME from GROUPS where MEMBER = :1", principalUID)])
 
         # Pull from cache
         result = yield self._memcacher.getMemberships(principalUID)
@@ -510,6 +549,14 @@
         """
         self._db_execute("delete from GROUPS where GROUPNAME = :1", principalUID)
 
+    def _delete_from_db_member(self, principalUID):
+        """
+        Deletes the specified member entry from the database.
+
+        @param principalUID: the UID of the member principal to remove.
+        """
+        self._db_execute("delete from GROUPS where MEMBER = :1", principalUID)
+
     def _db_version(self):
         """
         @return: the schema version assigned to this index.

Modified: CalendarServer/trunk/twistedcaldav/directory/principal.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/principal.py	2009-01-06 20:23:10 UTC (rev 3572)
+++ CalendarServer/trunk/twistedcaldav/directory/principal.py	2009-01-07 18:15:56 UTC (rev 3573)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2006-2007 Apple Inc. All rights reserved.
+# Copyright (c) 2006-2009 Apple Inc. All rights reserved.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -645,6 +645,8 @@
                 subprincipal = self.parent.principalForUID(uid)
                 if subprincipal:
                     proxies.append(subprincipal)
+                else:
+                    yield self._calendar_user_proxy_index().removeGroup(uid)
 
             groups.update(proxies)
 
@@ -677,8 +679,11 @@
             memberships = (yield self._calendar_user_proxy_index().getMemberships(self.principalUID()))
             for uid in memberships:
                 subprincipal = self.parent.principalForUID(uid)
-                if subprincipal and subprincipal.isProxyType(read_write):
-                    proxies.append(subprincipal.parent)
+                if subprincipal:
+                    if subprincipal.isProxyType(read_write):
+                        proxies.append(subprincipal.parent)
+                else:
+                    yield self._calendar_user_proxy_index().removeGroup(uid)
 
             proxyFors.update(proxies)
 

Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_proxyprincipaldb.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_proxyprincipaldb.py	2009-01-06 20:23:10 UTC (rev 3572)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_proxyprincipaldb.py	2009-01-07 18:15:56 UTC (rev 3573)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2005-2007 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2009 Apple Inc. All rights reserved.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -270,6 +270,82 @@
             self.assertEqual(membershipsD, set())
 
     @inlineCallbacks
+    def test_cachingDBRemoveSpecial(self):
+    
+        for processType in ("Single", "Combined",):
+            config.ProcessType = processType
+
+            # Get the DB
+            db_path = self.mktemp()
+            os.mkdir(db_path)
+            db = CalendarUserProxyDatabase(db_path)
+            
+            # Do one insert and check the result
+            yield db.setGroupMembers("A", ("B", "C", "D",))
+            yield db.setGroupMembers("X", ("B", "C",))
+    
+            membershipsB = yield db.getMemberships("B")
+            membershipsC = yield db.getMemberships("C")
+            membershipsD = yield db.getMemberships("D")
+            
+            # Remove and check the result
+            yield db.removeGroup("A")
+    
+            membersA = yield db.getMembers("A")
+            membersX = yield db.getMembers("X")
+            membershipsB = yield db.getMemberships("B")
+            membershipsC = yield db.getMemberships("C")
+            membershipsD = yield db.getMemberships("D")
+    
+            self.assertEqual(membersA, set())
+            self.assertEqual(membersX, set(("B", "C",)))
+            self.assertEqual(membershipsB, set("X",))
+            self.assertEqual(membershipsC, set("X",))
+            self.assertEqual(membershipsD, set())
+
+    @inlineCallbacks
+    def test_cachingDBRemovePrincipal(self):
+    
+        for processType in ("Single", "Combined",):
+            config.ProcessType = processType
+
+            # Get the DB
+            db_path = self.mktemp()
+            os.mkdir(db_path)
+            db = CalendarUserProxyDatabase(db_path)
+            
+            # Do one insert and check the result
+            yield db.setGroupMembers("A", ("B", "C", "D",))
+            yield db.setGroupMembers("X", ("B", "C",))
+    
+            membersA = yield db.getMembers("A")
+            membersX = yield db.getMembers("X")
+            membershipsB = yield db.getMemberships("B")
+            membershipsC = yield db.getMemberships("C")
+            membershipsD = yield db.getMemberships("D")
+    
+            self.assertEqual(membersA, set(("B", "C", "D",)))
+            self.assertEqual(membersX, set(("B", "C",)))
+            self.assertEqual(membershipsB, set(("A", "X",)))
+            self.assertEqual(membershipsC, set(("A", "X",)))
+            self.assertEqual(membershipsD, set(("A",)))
+            
+            # Remove and check the result
+            yield db.removePrincipal("B")
+    
+            membersA = yield db.getMembers("A")
+            membersX = yield db.getMembers("X")
+            membershipsB = yield db.getMemberships("B")
+            membershipsC = yield db.getMemberships("C")
+            membershipsD = yield db.getMemberships("D")
+    
+            self.assertEqual(membersA, set(("C", "D",)))
+            self.assertEqual(membersX, set(("C",)))
+            self.assertEqual(membershipsB, set())
+            self.assertEqual(membershipsC, set(("A", "X",)))
+            self.assertEqual(membershipsD, set(("A",),))
+
+    @inlineCallbacks
     def test_cachingDBInsertUncached(self):
     
         for processType in ("Single", "Combined",):

Modified: CalendarServer/trunk/twistedcaldav/directory/test/test_proxyprincipalmembers.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/test_proxyprincipalmembers.py	2009-01-06 20:23:10 UTC (rev 3572)
+++ CalendarServer/trunk/twistedcaldav/directory/test/test_proxyprincipalmembers.py	2009-01-07 18:15:56 UTC (rev 3573)
@@ -1,5 +1,5 @@
 ##
-# Copyright (c) 2005-2007 Apple Inc. All rights reserved.
+# Copyright (c) 2005-2009 Apple Inc. All rights reserved.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -18,10 +18,11 @@
 from twisted.web2.dav import davxml
 
 from twistedcaldav.directory.directory import DirectoryService
-from twistedcaldav.directory.xmlfile import XMLDirectoryService
 from twistedcaldav.directory.test.test_xmlfile import xmlFile
 from twistedcaldav.directory.principal import DirectoryPrincipalProvisioningResource
 from twistedcaldav.directory.principal import DirectoryPrincipalResource
+from twistedcaldav.directory.xmlaccountsparser import XMLAccountsParser
+from twistedcaldav.directory.xmlfile import XMLDirectoryService
 
 import twistedcaldav.test.util
 
@@ -76,6 +77,42 @@
         return d
     
     @inlineCallbacks
+    def _addProxy(self, principal, subPrincipalName, proxyPrincipal):
+
+        if isinstance(principal, tuple):
+            principal = self._getPrincipalByShortName(principal[0], principal[1])
+        principal = principal.getChild(subPrincipalName)
+        members = (yield principal.groupMembers())
+
+        if isinstance(proxyPrincipal, tuple):
+            proxyPrincipal = self._getPrincipalByShortName(proxyPrincipal[0], proxyPrincipal[1])
+        members.add(proxyPrincipal)
+        
+        principal.setGroupMemberSetPrincipals(members)
+
+    @inlineCallbacks
+    def _removeProxy(self, recordType, recordName, subPrincipalName, proxyRecordType, proxyRecordName):
+
+        principal = self._getPrincipalByShortName(recordType, recordName)
+        principal = principal.getChild(subPrincipalName)
+        members = (yield principal.groupMembers())
+
+        proxyPrincipal = self._getPrincipalByShortName(proxyRecordType, proxyRecordName)
+        for p in members:
+            if p.principalUID() == proxyPrincipal.principalUID():
+                members.remove(p)
+                break
+        
+        principal.setGroupMemberSetPrincipals(members)
+
+    def _clearProxy(self, principal, subPrincipalName):
+
+        if isinstance(principal, tuple):
+            principal = self._getPrincipalByShortName(principal[0], principal[1])
+        principal = principal.getChild(subPrincipalName)
+        principal.setGroupMemberSetPrincipals(set())
+
+    @inlineCallbacks
     def _proxyForTest(self, recordType, recordName, expectedProxies, read_write):
         principal = self._getPrincipalByShortName(recordType, recordName)
         proxies = (yield principal.proxyFor(read_write))
@@ -278,3 +315,121 @@
             False
         )
 
+    @inlineCallbacks
+    def test_UserProxy(self):
+        
+        for proxyType in ("calendar-proxy-read", "calendar-proxy-write"):
+
+            yield self._addProxy(
+                (DirectoryService.recordType_users, "wsanchez",),
+                proxyType,
+                (DirectoryService.recordType_users, "cdaboo",),
+            )
+    
+            yield self._groupMembersTest(
+                DirectoryService.recordType_users, "wsanchez",
+                proxyType,
+                ("Cyrus Daboo",),
+            )
+            
+            yield self._addProxy(
+                (DirectoryService.recordType_users, "wsanchez",),
+                proxyType,
+                (DirectoryService.recordType_users, "lecroy",),
+            )
+    
+            yield self._groupMembersTest(
+                DirectoryService.recordType_users, "wsanchez",
+                proxyType,
+                ("Cyrus Daboo", "Chris Lecroy",),
+            )
+    
+            yield self._removeProxy(
+                DirectoryService.recordType_users, "wsanchez",
+                proxyType,
+                DirectoryService.recordType_users, "cdaboo",
+            )
+    
+            yield self._groupMembersTest(
+                DirectoryService.recordType_users, "wsanchez",
+                proxyType,
+                ("Chris Lecroy",),
+            )
+
+    @inlineCallbacks
+    def test_InvalidUserProxy(self):
+
+        for doMembershipFirst in (True, False,):
+            for proxyType in ("calendar-proxy-read", "calendar-proxy-write"):
+                principal = self._getPrincipalByShortName(DirectoryService.recordType_users, "wsanchez")
+                proxyGroup = principal.getChild(proxyType)
+
+                testPrincipal = self._getPrincipalByShortName(DirectoryService.recordType_users, "cdaboo")
+
+                fakePrincipal = self._getPrincipalByShortName(DirectoryService.recordType_users, "dreid")
+                fakeProxyGroup = fakePrincipal.getChild(proxyType)
+
+                yield self._addProxy(
+                    principal,
+                    proxyType,
+                    testPrincipal,
+                )
+                members = yield proxyGroup._index().getMembers(proxyGroup.uid)
+                self.assertEquals(len(members), 1)
+
+                yield self._addProxy(
+                    fakePrincipal,
+                    proxyType,
+                    testPrincipal,
+                )
+                members = yield fakeProxyGroup._index().getMembers(fakeProxyGroup.uid)
+                self.assertEquals(len(members), 1)
+
+                uids = [p.principalUID() for p in (yield testPrincipal.groupMemberships())]
+                self.assertTrue("5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1#%s" % (proxyType,) in uids)
+
+                memberships = yield testPrincipal._calendar_user_proxy_index().getMemberships(testPrincipal.principalUID())
+                self.assertEquals(len(memberships), 2)
+
+                yield self._addProxy(
+                    principal,
+                    proxyType,
+                    fakePrincipal,
+                )
+                members = yield proxyGroup._index().getMembers(proxyGroup.uid)
+                self.assertEquals(len(members), 2)
+
+                # Remove the dreid user from the directory service
+                del directoryService._accounts()[DirectoryService.recordType_users]["dreid"]
+
+                @inlineCallbacks
+                def _membershipTest():
+                    uids = [p.principalUID() for p in (yield testPrincipal.groupMemberships())]
+                    self.assertTrue("5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1#%s" % (proxyType,) not in uids)
+
+                    memberships = yield testPrincipal._calendar_user_proxy_index().getMemberships(testPrincipal.principalUID())
+                    self.assertEquals(len(memberships), 1)
+
+                @inlineCallbacks
+                def _membersTest():
+                    yield self._groupMembersTest(
+                        DirectoryService.recordType_users, "wsanchez",
+                        proxyType,
+                        ("Cyrus Daboo",),
+                    )
+                    members = yield proxyGroup._index().getMembers(proxyGroup.uid)
+                    self.assertEquals(len(members), 1)
+
+                if doMembershipFirst:
+                    yield _membershipTest()
+                    yield _membersTest()
+                else:
+                    yield _membersTest()
+                    yield _membershipTest()
+
+                # Restore removed user
+                parser = XMLAccountsParser(directoryService.xmlFile)
+                directoryService._parsedAccounts = parser.items
+
+                self._clearProxy(principal, proxyType)
+                self._clearProxy(fakePrincipal, proxyType)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20090107/746b3c14/attachment-0001.html>


More information about the calendarserver-changes mailing list