[CalendarServer-changes] [7540] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Tue May 31 17:24:02 PDT 2011


Revision: 7540
          http://trac.macosforge.org/projects/calendarserver/changeset/7540
Author:   sagen at apple.com
Date:     2011-05-31 17:24:02 -0700 (Tue, 31 May 2011)
Log Message:
-----------
Delegates can now access wiki calendars that have been shared into the delegator's account (with same access as delegator) 9516310

Modified Paths:
--------------
    CalendarServer/trunk/calendarserver/provision/root.py
    CalendarServer/trunk/twistedcaldav/directory/principal.py
    CalendarServer/trunk/twistedcaldav/directory/wiki.py
    CalendarServer/trunk/twistedcaldav/sharing.py
    CalendarServer/trunk/twistedcaldav/test/test_sharing.py

Modified: CalendarServer/trunk/calendarserver/provision/root.py
===================================================================
--- CalendarServer/trunk/calendarserver/provision/root.py	2011-05-31 14:56:50 UTC (rev 7539)
+++ CalendarServer/trunk/calendarserver/provision/root.py	2011-06-01 00:24:02 UTC (rev 7540)
@@ -323,10 +323,13 @@
         if segments[0] in ("inbox", "timezones"):
             request.checkedSACL = True
 
-        elif (len(segments) > 2 and (segments[1] == "wikis" or
-            (segments[1] == "__uids__" and segments[2].startswith("wiki-")))):
-
-            # This is a wiki-related resource. SACLs are not checked.
+        elif (len(segments) > 2 and segments[0] == "calendars" and
+            (
+                segments[1] == "wikis" or
+                (segments[1] == "__uids__" and segments[2].startswith("wiki-"))
+            )
+        ):
+            # This is a wiki-related calendar resource. SACLs are not checked.
             request.checkedSACL = True
 
             # The authzuser value is set to that of the wiki principal if

Modified: CalendarServer/trunk/twistedcaldav/directory/principal.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/principal.py	2011-05-31 14:56:50 UTC (rev 7539)
+++ CalendarServer/trunk/twistedcaldav/directory/principal.py	2011-06-01 00:24:02 UTC (rev 7540)
@@ -70,18 +70,9 @@
     def defaultAccessControlList(self):
         return authReadACL
 
-    @inlineCallbacks
     def accessControlList(self, request, inheritance=True, expanding=False, inherited_aces=None):
 
-        wikiACL = (yield getWikiACL(self, request))
-        if wikiACL is not None:
-            # ACL depends on wiki server...
-            log.debug("Wiki ACL: %s" % (wikiACL.toxml(),))
-            returnValue(wikiACL)
-        else:
-            # ...otherwise permissions are fixed, and are not subject to
-            # inheritance rules, etc.
-            returnValue(self.defaultAccessControlList())
+        return succeed(self.defaultAccessControlList())
 
 
 

Modified: CalendarServer/trunk/twistedcaldav/directory/wiki.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/wiki.py	2011-05-31 14:56:50 UTC (rev 7539)
+++ CalendarServer/trunk/twistedcaldav/directory/wiki.py	2011-06-01 00:24:02 UTC (rev 7540)
@@ -128,7 +128,38 @@
         self.enabled = True
 
 
+ at inlineCallbacks
+def getWikiAccess(userID, wikiID):
+    """
+    Ask the wiki server we're paired with what level of access the userID has
+    for the given wikiID.  Possible values are "read", "write", and "admin"
+    (which we treat as "write").
+    """
+    wikiConfig = config.Authentication.Wiki
+    proxy = Proxy(wikiConfig["URL"])
+    try:
 
+        log.debug("Looking up Wiki ACL for: user [%s], wiki [%s]" % (userID,
+            wikiID))
+        access = (yield proxy.callRemote(wikiConfig["WikiMethod"],
+            userID, wikiID))
+
+        log.debug("Wiki ACL result: user [%s], wiki [%s], access [%s]" % (userID,
+            wikiID, access))
+        returnValue(access)
+
+    except Fault, fault:
+
+        log.debug("Wiki ACL result: user [%s], wiki [%s], FAULT [%s]" % (userID,
+            wikiID, fault))
+
+        if fault.faultCode == 2: # non-existent user
+            raise HTTPError(StatusResponse(responsecode.FORBIDDEN, fault.faultString))
+
+        else: # fault.faultCode == 12, non-existent wiki
+            raise HTTPError(StatusResponse(responsecode.NOT_FOUND, fault.faultString))
+
+
 @inlineCallbacks
 def getWikiACL(resource, request):
     """
@@ -151,7 +182,6 @@
     if hasattr(request, 'wikiACL'):
         returnValue(request.wikiACL)
 
-    wikiConfig = config.Authentication.Wiki
     userID = "unauthenticated"
     wikiID = resource.record.shortNames[0]
 
@@ -164,17 +194,9 @@
         # TODO: better error handling
         pass
 
-    proxy = Proxy(wikiConfig["URL"])
     try:
+        access = (yield getWikiAccess(userID, wikiID))
 
-        log.debug("Looking up Wiki ACL for: user [%s], wiki [%s]" % (userID,
-            wikiID))
-        access = (yield proxy.callRemote(wikiConfig["WikiMethod"],
-            userID, wikiID))
-
-        log.debug("Wiki ACL result: user [%s], wiki [%s], access [%s]" % (userID,
-            wikiID, access))
-
         # The ACL we returns has ACEs for the end-user and the wiki principal
         # in case authzUser is the wiki principal.
         if access == "read":
@@ -246,18 +268,6 @@
                 )
             )
 
-
-    except Fault, fault:
-
-        log.error("Wiki ACL result: user [%s], wiki [%s], FAULT [%s]" % (userID,
-            wikiID, fault))
-
-        if fault.faultCode == 2: # non-existent user
-            raise HTTPError(StatusResponse(responsecode.FORBIDDEN, fault.faultString))
-
-        else: # fault.faultCode == 12, non-existent wiki
-            raise HTTPError(StatusResponse(responsecode.NOT_FOUND, fault.faultString))
-
     except HTTPError:
         # pass through the HTTPError we might have raised above
         raise

Modified: CalendarServer/trunk/twistedcaldav/sharing.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/sharing.py	2011-05-31 14:56:50 UTC (rev 7539)
+++ CalendarServer/trunk/twistedcaldav/sharing.py	2011-06-01 00:24:02 UTC (rev 7540)
@@ -38,6 +38,7 @@
 from twistedcaldav import customxml, caldavxml
 from twistedcaldav.config import config
 from twistedcaldav.customxml import calendarserver_namespace
+from twistedcaldav.directory.wiki import WikiDirectoryService, getWikiAccess
 from twistedcaldav.linkresource import LinkFollowerMixIn
 from twistedcaldav.sql import AbstractSQLDatabase, db_prefix
 
@@ -267,11 +268,28 @@
 
         assert self._isVirtualShare, "Only call this for a virtual share"
 
+        wikiAccessMethod = kwargs.get("wikiAccessMethod", getWikiAccess)
+
         # Direct shares use underlying privileges of shared collection
         if self._share.sharetype == SHARETYPE_DIRECT:
             original = (yield request.locateResource(self._share.hosturl))
-            result = (yield original.accessControlList(request, *args, **kwargs))
-            returnValue(result)
+            owner = yield original.ownerPrincipal(request)
+            if owner.record.recordType == WikiDirectoryService.recordType_wikis:
+                # Access level comes from what the wiki has granted to the
+                # sharee
+                userID = self._shareePrincipal.record.guid
+                wikiID = owner.record.shortNames[0]
+                inviteAccess = (yield wikiAccessMethod(userID, wikiID))
+                if inviteAccess == "read":
+                    inviteAccess = "read-only"
+                elif inviteAccess in ("write", "admin"):
+                    inviteAccess = "read-write"
+                else:
+                    inviteAccess = None
+            else:
+                result = (yield original.accessControlList(request, *args,
+                    **kwargs))
+                returnValue(result)
         else:
             # Invite shares use access mode from the invite
     
@@ -281,68 +299,69 @@
             )
             if invite is None:
                 returnValue(davxml.ACL())
+            inviteAccess = invite.access
             
-            userprivs = [
-            ]
-            if invite.access in ("read-only", "read-write", "read-write-schedule",):
-                userprivs.append(davxml.Privilege(davxml.Read()))
-                userprivs.append(davxml.Privilege(davxml.ReadACL()))
-                userprivs.append(davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()))
-            if invite.access in ("read-only",):
-                userprivs.append(davxml.Privilege(davxml.WriteProperties()))
-            if invite.access in ("read-write", "read-write-schedule",):
-                userprivs.append(davxml.Privilege(davxml.Write()))
-            proxyprivs = list(userprivs)
-            proxyprivs.remove(davxml.Privilege(davxml.ReadACL()))
+        userprivs = [
+        ]
+        if inviteAccess in ("read-only", "read-write", "read-write-schedule",):
+            userprivs.append(davxml.Privilege(davxml.Read()))
+            userprivs.append(davxml.Privilege(davxml.ReadACL()))
+            userprivs.append(davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()))
+        if inviteAccess in ("read-only",):
+            userprivs.append(davxml.Privilege(davxml.WriteProperties()))
+        if inviteAccess in ("read-write", "read-write-schedule",):
+            userprivs.append(davxml.Privilege(davxml.Write()))
+        proxyprivs = list(userprivs)
+        proxyprivs.remove(davxml.Privilege(davxml.ReadACL()))
 
-            aces = (
-                # Inheritable specific access for the resource's associated principal.
+        aces = (
+            # Inheritable specific access for the resource's associated principal.
+            davxml.ACE(
+                davxml.Principal(davxml.HRef(self._shareePrincipal.principalURL())),
+                davxml.Grant(*userprivs),
+                davxml.Protected(),
+                TwistedACLInheritable(),
+            ),
+        )
+
+        if self.isCalendarCollection():
+            aces += (
+                # Inheritable CALDAV:read-free-busy access for authenticated users.
                 davxml.ACE(
-                    davxml.Principal(davxml.HRef(self._shareePrincipal.principalURL())),
-                    davxml.Grant(*userprivs),
-                    davxml.Protected(),
+                    davxml.Principal(davxml.Authenticated()),
+                    davxml.Grant(davxml.Privilege(caldavxml.ReadFreeBusy())),
                     TwistedACLInheritable(),
                 ),
             )
 
-            if self.isCalendarCollection():
-                aces += (
-                    # Inheritable CALDAV:read-free-busy access for authenticated users.
-                    davxml.ACE(
-                        davxml.Principal(davxml.Authenticated()),
-                        davxml.Grant(davxml.Privilege(caldavxml.ReadFreeBusy())),
-                        TwistedACLInheritable(),
-                    ),
-                )
+        # Give read access to config.ReadPrincipals
+        aces += config.ReadACEs
 
-            # Give read access to config.ReadPrincipals
-            aces += config.ReadACEs
+        # Give all access to config.AdminPrincipals
+        aces += config.AdminACEs
 
-            # Give all access to config.AdminPrincipals
-            aces += config.AdminACEs
-
-            if config.EnableProxyPrincipals:
-                aces += (
-                    # DAV:read/DAV:read-current-user-privilege-set access for this principal's calendar-proxy-read users.
-                    davxml.ACE(
-                        davxml.Principal(davxml.HRef(joinURL(self._shareePrincipal.principalURL(), "calendar-proxy-read/"))),
-                        davxml.Grant(
-                            davxml.Privilege(davxml.Read()),
-                            davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
-                        ),
-                        davxml.Protected(),
-                        TwistedACLInheritable(),
+        if config.EnableProxyPrincipals:
+            aces += (
+                # DAV:read/DAV:read-current-user-privilege-set access for this principal's calendar-proxy-read users.
+                davxml.ACE(
+                    davxml.Principal(davxml.HRef(joinURL(self._shareePrincipal.principalURL(), "calendar-proxy-read/"))),
+                    davxml.Grant(
+                        davxml.Privilege(davxml.Read()),
+                        davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
                     ),
-                    # DAV:read/DAV:read-current-user-privilege-set/DAV:write access for this principal's calendar-proxy-write users.
-                    davxml.ACE(
-                        davxml.Principal(davxml.HRef(joinURL(self._shareePrincipal.principalURL(), "calendar-proxy-write/"))),
-                        davxml.Grant(*proxyprivs),
-                        davxml.Protected(),
-                        TwistedACLInheritable(),
-                    ),
-                )
+                    davxml.Protected(),
+                    TwistedACLInheritable(),
+                ),
+                # DAV:read/DAV:read-current-user-privilege-set/DAV:write access for this principal's calendar-proxy-write users.
+                davxml.ACE(
+                    davxml.Principal(davxml.HRef(joinURL(self._shareePrincipal.principalURL(), "calendar-proxy-write/"))),
+                    davxml.Grant(*proxyprivs),
+                    davxml.Protected(),
+                    TwistedACLInheritable(),
+                ),
+            )
 
-            returnValue(davxml.ACL(*aces))
+        returnValue(davxml.ACL(*aces))
 
     def validUserIDForShare(self, userid):
         """

Modified: CalendarServer/trunk/twistedcaldav/test/test_sharing.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/test_sharing.py	2011-05-31 14:56:50 UTC (rev 7539)
+++ CalendarServer/trunk/twistedcaldav/test/test_sharing.py	2011-06-01 00:24:02 UTC (rev 7540)
@@ -18,16 +18,20 @@
 from twext.web2 import responsecode
 from twext.web2.dav import davxml
 from twext.web2.http_headers import MimeType
+from twext.web2.iweb import IResource
 from twext.web2.stream import MemoryStream
 from twext.web2.test.test_server import SimpleRequest
 from twisted.internet.defer import inlineCallbacks, returnValue, succeed
 from twistedcaldav import customxml
 from twistedcaldav.config import config
 from twistedcaldav.test.util import HomeTestCase, norequest
+from twistedcaldav.sharing import SharedCollectionMixin, SHARETYPE_DIRECT, WikiDirectoryService
 
 from twistedcaldav.resource import CalDAVResource
 from txdav.common.datastore.test.util import buildStore, StubNotifierFactory
+from zope.interface import implements
 
+
 sharedOwnerType = davxml.ResourceType.sharedownercalendar #@UndefinedVariable
 regularCalendarType = davxml.ResourceType.calendar #@UndefinedVariable
 
@@ -542,7 +546,85 @@
         self.assertEquals(self._clearUIDElementValue(propInvite), customxml.Invite())
 
 
+    @inlineCallbacks
+    def test_wikiACL(self):
+        """
+        Ensure shareeAccessControlList( ) honors the access granted by the wiki
+        to the sharee, so that delegates of the sharee get the same level of
+        access.
+        """
 
+        def stubWikiAccessMethod(userID, wikiID):
+            return access
+
+        class StubCollection(object):
+            def __init__(self):
+                self._isVirtualShare = True
+                self._shareePrincipal = StubUserPrincipal()
+            def isCalendarCollection(self):
+                return True
+
+        class StubShare(object):
+            def __init__(self):
+                self.sharetype = SHARETYPE_DIRECT
+                self.hosturl = "/wikifoo"
+
+        class TestCollection(SharedCollectionMixin, StubCollection):
+            pass
+
+        class StubRecord(object):
+            def __init__(self, recordType, name, guid):
+                self.recordType = recordType
+                self.shortNames = [name]
+                self.guid = guid
+
+        class StubUserPrincipal(object):
+            def __init__(self):
+                self.record = StubRecord(
+                    "users",
+                    "testuser",
+                    "4F364813-0415-45CB-9FD4-DBFEF7A0A8E0"
+                )
+            def principalURL(self):
+                return "/principals/__uids__/%s/" % (self.record.guid,)
+
+        class StubWikiPrincipal(object):
+            def __init__(self):
+                self.record = StubRecord(
+                    WikiDirectoryService.recordType_wikis,
+                    "wikifoo",
+                    "foo"
+                )
+
+        class StubWikiResource(object):
+            implements(IResource)
+
+            def locateChild(self, req, segments):
+                pass
+            def renderHTTP(req):
+                pass
+            def ownerPrincipal(self, req):
+                return succeed(StubWikiPrincipal())
+
+
+        collection = TestCollection()
+        collection._share = StubShare()
+        self.site.resource.putChild("wikifoo", StubWikiResource())
+        request = SimpleRequest(self.site, "GET", "/wikifoo")
+
+        # Simulate the wiki server granting Read access
+        access = "read"
+        acl = (yield collection.shareeAccessControlList(request,
+            wikiAccessMethod=stubWikiAccessMethod))
+        self.assertFalse("<write/>" in acl.toxml())
+
+        # Simulate the wiki server granting Read-Write access
+        access = "write"
+        acl = (yield collection.shareeAccessControlList(request,
+            wikiAccessMethod=stubWikiAccessMethod))
+        self.assertTrue("<write/>" in acl.toxml())
+
+
 class DatabaseSharingTests(SharingTests):
 
     @inlineCallbacks
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20110531/37c80b82/attachment-0001.html>


More information about the calendarserver-changes mailing list