[CalendarServer-changes] [11293] CalendarServer/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Tue Jun 4 13:52:00 PDT 2013
Revision: 11293
http://trac.calendarserver.org//changeset/11293
Author: cdaboo at apple.com
Date: 2013-06-04 13:52:00 -0700 (Tue, 04 Jun 2013)
Log Message:
-----------
Make sure wiki access failure does not generate a 500. Actually make that generate a 503 inside a multi-status
response for the failed child resource.
Modified Paths:
--------------
CalendarServer/trunk/twext/web2/dav/resource.py
CalendarServer/trunk/twistedcaldav/directory/wiki.py
CalendarServer/trunk/twistedcaldav/method/propfind.py
CalendarServer/trunk/twistedcaldav/method/report_addressbook_query.py
CalendarServer/trunk/twistedcaldav/method/report_calendar_query.py
CalendarServer/trunk/twistedcaldav/method/report_multiget_common.py
CalendarServer/trunk/twistedcaldav/method/report_sync_collection.py
CalendarServer/trunk/twistedcaldav/resource.py
CalendarServer/trunk/twistedcaldav/storebridge.py
Modified: CalendarServer/trunk/twext/web2/dav/resource.py
===================================================================
--- CalendarServer/trunk/twext/web2/dav/resource.py 2013-06-04 19:31:04 UTC (rev 11292)
+++ CalendarServer/trunk/twext/web2/dav/resource.py 2013-06-04 20:52:00 UTC (rev 11293)
@@ -8,10 +8,10 @@
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
-#
+#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
-#
+#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -137,32 +137,33 @@
# use them. This (generic) class does not.
def liveProperties(self):
-
+
return (
- (dav_namespace, "resourcetype" ),
- (dav_namespace, "getetag" ),
- (dav_namespace, "getcontenttype" ),
- (dav_namespace, "getcontentlength" ),
- (dav_namespace, "getlastmodified" ),
- (dav_namespace, "creationdate" ),
- (dav_namespace, "displayname" ),
- (dav_namespace, "supportedlock" ),
- (dav_namespace, "supported-report-set" ), # RFC 3253, section 3.1.5
+ (dav_namespace, "resourcetype"),
+ (dav_namespace, "getetag"),
+ (dav_namespace, "getcontenttype"),
+ (dav_namespace, "getcontentlength"),
+ (dav_namespace, "getlastmodified"),
+ (dav_namespace, "creationdate"),
+ (dav_namespace, "displayname"),
+ (dav_namespace, "supportedlock"),
+ (dav_namespace, "supported-report-set"), # RFC 3253, section 3.1.5
#(dav_namespace, "owner" ), # RFC 3744, section 5.1
#(dav_namespace, "group" ), # RFC 3744, section 5.2
- (dav_namespace, "supported-privilege-set" ), # RFC 3744, section 5.3
+ (dav_namespace, "supported-privilege-set"), # RFC 3744, section 5.3
(dav_namespace, "current-user-privilege-set"), # RFC 3744, section 5.4
- (dav_namespace, "current-user-principal" ), # RFC 5397, Section 3
- (dav_namespace, "acl" ), # RFC 3744, section 5.5
- (dav_namespace, "acl-restrictions" ), # RFC 3744, section 5.6
- (dav_namespace, "inherited-acl-set" ), # RFC 3744, section 5.7
- (dav_namespace, "principal-collection-set" ), # RFC 3744, section 5.8
- (dav_namespace, "quota-available-bytes" ), # RFC 4331, section 3
- (dav_namespace, "quota-used-bytes" ), # RFC 4331, section 4
-
+ (dav_namespace, "current-user-principal"), # RFC 5397, Section 3
+ (dav_namespace, "acl"), # RFC 3744, section 5.5
+ (dav_namespace, "acl-restrictions"), # RFC 3744, section 5.6
+ (dav_namespace, "inherited-acl-set"), # RFC 3744, section 5.7
+ (dav_namespace, "principal-collection-set"), # RFC 3744, section 5.8
+ (dav_namespace, "quota-available-bytes"), # RFC 4331, section 3
+ (dav_namespace, "quota-used-bytes"), # RFC 4331, section 4
+
(twisted_dav_namespace, "resource-class"),
)
+
def deadProperties(self):
"""
Provides internal access to the WebDAV dead property store.
@@ -186,6 +187,7 @@
self._dead_properties = NonePropertyStore(self)
return self._dead_properties
+
def hasProperty(self, property, request):
"""
See L{IDAVResource.hasProperty}.
@@ -205,7 +207,7 @@
d = self.hasQuota(request)
d.addCallback(lambda result: result)
return d
-
+
return succeed(
qname in self.liveProperties() or
self.deadProperties().contains(qname)
@@ -251,8 +253,8 @@
if name == "getcontentlength":
length = self.contentLength()
if length is None:
- # TODO: really we should "render" the resource and
- # determine its size from that but for now we just
+ # TODO: really we should "render" the resource and
+ # determine its size from that but for now we just
# return an empty element.
returnValue(element.GETContentLength(""))
else:
@@ -412,6 +414,7 @@
return maybeDeferred(defer)
+
def removeProperty(self, property, request):
"""
See L{IDAVResource.removeProperty}.
@@ -441,6 +444,7 @@
return maybeDeferred(defer)
+
@inlineCallbacks
def listProperties(self, request):
"""
@@ -451,7 +455,7 @@
# Add dynamic live properties that exist
dynamicLiveProperties = (
(dav_namespace, "quota-available-bytes"),
- (dav_namespace, "quota-used-bytes" ),
+ (dav_namespace, "quota-used-bytes"),
)
for dqname in dynamicLiveProperties:
has = (yield self.hasProperty(dqname, request))
@@ -467,6 +471,7 @@
returnValue(qnames)
+
def listAllprop(self, request):
"""
Some DAV properties should not be returned to a C{DAV:allprop}
@@ -496,6 +501,7 @@
d.addCallback(doList)
return d
+
def hasDeadProperty(self, property):
"""
Same as L{hasProperty}, but bypasses the live property store
@@ -508,6 +514,7 @@
return self.deadProperties().contains(qname)
+
def readDeadProperty(self, property):
"""
Same as L{readProperty}, but bypasses the live property store
@@ -520,6 +527,7 @@
return self.deadProperties().get(qname)
+
def writeDeadProperty(self, property):
"""
Same as L{writeProperty}, but bypasses the live property store
@@ -533,6 +541,7 @@
"""
self.deadProperties().set(property)
+
def removeDeadProperty(self, property):
"""
Same as L{removeProperty}, but bypasses the live property
@@ -546,6 +555,7 @@
self.deadProperties().delete(qname)
+
#
# Overrides some methods in MetaDataMixin in order to allow DAV properties
# to override the values of some HTTP metadata.
@@ -558,6 +568,7 @@
else:
return super(DAVPropertyMixIn, self).contentType()
+
def displayName(self):
if self.hasDeadProperty((element.dav_namespace, "displayname")):
return str(self.readDeadProperty(
@@ -566,6 +577,8 @@
else:
return super(DAVPropertyMixIn, self).displayName()
+
+
class DAVResource (DAVPropertyMixIn, StaticRenderMixin):
"""
WebDAV resource.
@@ -584,6 +597,7 @@
for principalCollection in principalCollections
])
+
##
# DAV
##
@@ -597,6 +611,7 @@
"""
unimplemented(self)
+
def isCollection(self):
"""
See L{IDAVResource.isCollection}.
@@ -606,6 +621,7 @@
"""
unimplemented(self)
+
def findChildren(
self, depth, request, callback,
privileges=None, inherited_aces=None
@@ -636,7 +652,7 @@
if privileges is None:
return child
-
+
d = child.checkPrivileges(
request, privileges,
inherited_aces=inherited_aces
@@ -681,9 +697,10 @@
return completionDeferred
+
@inlineCallbacks
def findChildrenFaster(
- self, depth, request, okcallback, badcallback, missingcallback,
+ self, depth, request, okcallback, badcallback, missingcallback, unavailablecallback,
names, privileges, inherited_aces
):
"""
@@ -692,7 +709,7 @@
This implementation works for C{depth} values of C{"0"},
C{"1"}, and C{"infinity"}. As long as C{self.listChildren} is
implemented
-
+
@param depth: a C{str} for the depth: "0", "1" and "infinity"
only allowed.
@param request: the L{Request} for the current request in
@@ -717,7 +734,7 @@
returnValue(None)
# First find all depth 1 children
- names1= []
+ names1 = []
namesDeep = []
collections1 = []
if names:
@@ -732,7 +749,13 @@
childnames = list((yield self.listChildren()))
for childname in childnames:
childpath = joinURL(basepath, urllib.quote(childname))
- child = (yield request.locateChildResource(self, childname))
+ try:
+ child = (yield request.locateChildResource(self, childname))
+ except HTTPError, e:
+ log.error("Resource cannot be located: %s" % (str(e),))
+ if unavailablecallback:
+ unavailablecallback(childpath)
+ continue
if child is not None:
if child.isCollection():
collections1.append((child, childpath + "/"))
@@ -788,18 +811,19 @@
yield collection.inheritedACEsforChildren(request)
)
yield collection.findChildrenFaster(
- depth, request, okcallback, badcallback, missingcallback,
+ depth, request, okcallback, badcallback, missingcallback, unavailablecallback,
child_collections[collection_name] if names else None, privileges,
inherited_aces=collection_inherited_aces
)
-
+
returnValue(None)
+
@inlineCallbacks
def checkACLPrivilege(
self, request, acl, privyset, privileges, inherited_aces
):
-
+
if acl is None:
returnValue(False)
@@ -840,6 +864,7 @@
returnValue(len(denied) + len(pending) == 0)
+
def fullAccessControlList(self, acl, inherited_aces):
"""
See L{IDAVResource.accessControlList}.
@@ -876,7 +901,8 @@
acl = element.ACL(*aces)
return acl
-
+
+
def supportedReports(self):
"""
See L{IDAVResource.supportedReports}.
@@ -892,6 +918,7 @@
result.append(element.Report(customxml.CalendarServerPrincipalSearch(),))
return result
+
##
# Authentication
##
@@ -931,6 +958,7 @@
d.addCallback(whenAuthenticated)
return d
+
def authenticate(self, request):
"""
Authenticate the given request against the portal, setting
@@ -1046,6 +1074,7 @@
request.authzUser = element.Principal(element.Unauthenticated())
return succeed((request.authnUser, request.authzUser))
+
##
# ACL
##
@@ -1061,6 +1090,7 @@
else:
return unauthenticatedPrincipal
+
def principalCollections(self):
"""
See L{IDAVResource.principalCollections}.
@@ -1070,6 +1100,7 @@
else:
return ()
+
def defaultRootAccessControlList(self):
"""
@return: the L{element.ACL} element containing the default
@@ -1082,6 +1113,7 @@
#
return readonlyACL
+
def defaultAccessControlList(self):
"""
@return: the L{element.ACL} element containing the default
@@ -1093,6 +1125,7 @@
#
return element.ACL()
+
def setAccessControlList(self, acl):
"""
See L{IDAVResource.setAccessControlList}.
@@ -1121,7 +1154,7 @@
This implementation stores the ACL in the private property
"""
# C{(L{twisted_private_namespace}, "acl")}.
-
+
# Steps for ACL evaluation:
# 1. Check that ace's on incoming do not match a protected ace
# 2. Check that ace's on incoming do not match an inherited ace
@@ -1333,7 +1366,7 @@
if recurse:
yield self.findChildren(
"infinity", request,
- lambda x, y: resources.append((x,y))
+ lambda x, y: resources.append((x, y))
)
for resource, uri in resources:
@@ -1378,14 +1411,15 @@
denied += pending # If no matching ACE, then denied
- if denied:
+ if denied:
errors.append((uri, denied))
if errors:
raise AccessDeniedError(errors,)
-
+
returnValue(None)
+
def supportedPrivileges(self, request):
"""
See L{IDAVResource.supportedPrivileges}.
@@ -1395,6 +1429,7 @@
"""
return succeed(davPrivilegeSet)
+
def currentPrivileges(self, request):
"""
See L{IDAVResource.currentPrivileges}.
@@ -1405,6 +1440,7 @@
current = self.currentPrincipal(request)
return self.privilegesForPrincipal(current, request)
+
@inlineCallbacks
def accessControlList(
self, request, inheritance=True,
@@ -1474,20 +1510,20 @@
if inherited_aces is None:
if myURL != "/":
parentURL = parentForURL(myURL)
-
+
parent = (yield request.locateResource(parentURL))
-
+
if parent:
parent_acl = (yield
parent.accessControlList(
request, inheritance=True, expanding=True
)
)
-
+
# Check disabled
if parent_acl is None:
returnValue(None)
-
+
for ace in parent_acl.children:
if ace.inherited:
aces.append(ace)
@@ -1518,6 +1554,7 @@
returnValue(acl)
+
def inheritedACEsforChildren(self, request):
"""
Do some optimisation of access control calculation by
@@ -1528,7 +1565,7 @@
@return: a C{list} of L{Ace}s that child resources of this one
will inherit.
"""
-
+
# Get the parent ACLs with inheritance and preserve the
# <inheritable> element.
@@ -1559,6 +1596,7 @@
d.addCallback(gotACL)
return d
+
def inheritedACLSet(self):
"""
@return: a sequence of L{element.HRef}s from which ACLs are
@@ -1568,6 +1606,7 @@
"""
return []
+
def principalsForAuthID(self, request, authid):
"""
Return authentication and authorization principal identifiers
@@ -1596,6 +1635,7 @@
d.addCallback(lambda authzPrincipal: (authnPrincipal, authzPrincipal))
return d
+
def findPrincipalForAuthID(self, authid):
"""
Return authentication and authorization principal identifiers
@@ -1616,12 +1656,13 @@
return principal
return None
+
def authorizationPrincipal(self, request, authid, authnPrincipal):
"""
Determine the authorization principal for the given request
and authentication principal. This implementation simply uses
that authentication principal as the authorization principal.
-
+
@param request: the L{IRequest} for the request in progress.
@param authid: a string containing the
authentication/authorization identifier for the principal
@@ -1633,7 +1674,8 @@
and URI respectively.
"""
return succeed(authnPrincipal)
-
+
+
def samePrincipal(self, principal1, principal2):
"""
Check whether the two principals are exactly the same in terms of
@@ -1663,7 +1705,8 @@
return True
else:
return False
-
+
+
def matchPrincipal(self, principal1, principal2, request):
"""
Check whether the principal1 is a principal in the set defined
@@ -1739,6 +1782,7 @@
d.addCallback(cache)
return d
+
@inlineCallbacks
def principalIsGroupMember(self, principal1, principal2, request):
"""
@@ -1759,7 +1803,7 @@
returnValue(isContained)
returnValue(False)
-
+
def validPrincipal(self, ace_principal, request):
"""
Check whether the supplied principal is valid for this resource.
@@ -1793,6 +1837,7 @@
return maybeDeferred(defer)
+
def validHrefPrincipal(self, href_principal, request):
"""
Check whether the supplied principal (in the form of an Href)
@@ -1809,7 +1854,7 @@
# Must have the principal resource type and must match the
# principal-URL
-
+
def _matchPrincipalURL(resource):
return (
isPrincipalResource(resource) and
@@ -1820,6 +1865,7 @@
d.addCallback(_matchPrincipalURL)
return d
+
def resolvePrincipal(self, principal, request):
"""
Resolves a L{element.Principal} element into a L{element.HRef}
@@ -1911,6 +1957,7 @@
return succeed(None)
+
@inlineCallbacks
def privilegesForPrincipal(self, principal, request):
"""
@@ -1954,6 +2001,7 @@
returnValue(allowed)
+
def matchACEinACL(self, acl, ace):
"""
Find an ACE in the ACL that matches the supplied ACE's principal.
@@ -1964,15 +2012,16 @@
for a in acl.children:
if self.samePrincipal(a.principal, ace.principal):
return a
-
+
return None
-
+
+
def principalSearchPropertySet(self):
"""
@return: a L{element.PrincipalSearchPropertySet} element describing the
principal properties that can be searched on this principal collection,
or C{None} if this is not a principal collection.
-
+
This implementation returns None. Principal collection resources must
override and return their own suitable response.
"""
@@ -1981,18 +2030,18 @@
##
# Quota
##
-
+
"""
The basic policy here is to define a private 'quota-root' property
on a collection. That property will contain the maximum allowed
bytes for the collections and all its contents.
-
+
In order to determine the quota property values on a resource, the
server must look for the private property on that resource and any
of its parents. If found on a parent, then that parent should be
queried for quota information. If not found, no quota exists for
the resource.
-
+
To determine that actual quota in use we will cache the used byte
count on the quota-root collection in another private property. It
is the servers responsibility to keep that property up to date by
@@ -2013,10 +2062,10 @@
quota-used-bytes, or C{None} if quota is not defined on
the resource.
"""
-
+
# See if already cached
if hasattr(request, "quota"):
- if request.quota.has_key(self):
+ if self in request.quota:
return succeed(request.quota[self])
else:
request.quota = {}
@@ -2032,7 +2081,7 @@
available = 0
request.quota[self] = (available, used)
return (available, used)
-
+
d = qroot_resource.currentQuotaUse(request)
d.addCallback(gotUsage)
return d
@@ -2040,33 +2089,35 @@
request.quota[self] = None
return None
-
d = self.quotaRootResource(request)
d.addCallback(gotQuotaRootResource)
return d
-
+
+
def hasQuota(self, request):
"""
Check whether this resource is under quota control by checking
each parent to see if it has a quota root.
-
+
@return: C{True} if under quota control, C{False} if not.
"""
def gotQuotaRootResource(qroot_resource):
-
+
return qroot_resource is not None
-
+
d = self.quotaRootResource(request)
d.addCallback(gotQuotaRootResource)
return d
+
def hasQuotaRoot(self, request):
"""
@return: a C{True} if this resource has quota root, C{False} otherwise.
"""
return self.hasDeadProperty(TwistedQuotaRootProperty)
-
+
+
def quotaRoot(self, request):
"""
@return: a C{int} containing the maximum allowed bytes if this
@@ -2077,12 +2128,13 @@
return int(str(self.readDeadProperty(TwistedQuotaRootProperty)))
else:
return None
-
+
+
@inlineCallbacks
def quotaRootResource(self, request):
"""
Return the quota root for this resource.
-
+
@return: L{DAVResource} or C{None}
"""
@@ -2106,6 +2158,7 @@
returnValue(None)
+
def setQuotaRoot(self, request, maxsize):
"""
@param maxsize: a C{int} containing the maximum allowed bytes
@@ -2116,14 +2169,15 @@
assert maxsize is None or isinstance(maxsize, int), (
"maxsize must be an int or None"
)
-
+
if maxsize is not None:
self.writeDeadProperty(TwistedQuotaRootProperty(str(maxsize)))
else:
# Remove both the root and the cached used value
self.removeDeadProperty(TwistedQuotaRootProperty)
self.removeDeadProperty(TwistedQuotaUsedProperty)
-
+
+
def quotaSize(self, request):
"""
Get the size of this resource (if its a collection get total
@@ -2134,19 +2188,20 @@
"""
unimplemented(self)
+
def checkQuota(self, request, available):
"""
Check to see whether all quota roots have sufficient available
bytes. We currently do not use hierarchical quota checks -
i.e. only the most immediate quota root parent is checked for
quota.
-
+
@param available: a C{int} containing the additional quota
required.
@return: C{True} if there is sufficient quota remaining on all
quota roots, C{False} otherwise.
"""
-
+
def _defer(quotaroot):
if quotaroot:
# Check quota on this root (if it has one)
@@ -2154,13 +2209,14 @@
if quota is not None:
if available > quota[0]:
return False
-
+
return True
d = self.quotaRootResource(request)
d.addCallback(_defer)
return d
+
def quotaSizeAdjust(self, request, adjust):
"""
Update the quota used value on all quota root parents of this
@@ -2170,8 +2226,7 @@
(positive) or removed (negative) that should be used to
adjust the cached total.
"""
-
-
+
def _defer(quotaroot):
if quotaroot:
# Check quota on this root (if it has one)
@@ -2181,6 +2236,7 @@
d.addCallback(_defer)
return d
+
def currentQuotaUse(self, request):
"""
Get the cached quota use value, or if not present (or invalid)
@@ -2194,7 +2250,7 @@
assert self.hasQuotaRoot(request), (
"Quota use only on quota root collection"
)
-
+
# Try to get the cached value property
if self.hasDeadProperty(TwistedQuotaUsedProperty):
return succeed(
@@ -2210,6 +2266,7 @@
d.addCallback(_defer)
return d
+
def updateQuotaUse(self, request, adjust):
"""
Update the quota used value on this resource.
@@ -2222,11 +2279,11 @@
or C{None} if not quota controlled.
"""
assert self.isCollection(), "Only collections can have a quota root"
-
+
# Get current value
def _defer(size):
size += adjust
-
+
# Sanity check the resulting size
if size >= 0:
self.writeDeadProperty(TwistedQuotaUsedProperty(str(size)))
@@ -2242,7 +2299,8 @@
d = self.currentQuotaUse(request)
d.addCallback(_defer)
return d
-
+
+
##
# HTTP
##
@@ -2260,7 +2318,7 @@
request.unparseURL(
path=urllib.quote(
urllib.unquote(request.path),
- safe=':/')+'/'
+ safe=':/') + '/'
)
)
@@ -2291,6 +2349,8 @@
d = maybeDeferred(super(DAVResource, self).renderHTTP, request)
return d.addCallbacks(setHeaders, onError)
+
+
class DAVLeafResource (DAVResource, LeafResource):
"""
DAV resource with no children.
@@ -2301,6 +2361,8 @@
):
return succeed(None)
+
+
class DAVPrincipalResource (DAVResource):
"""
Resource representing a WebDAV principal. (RFC 3744, section 2)
@@ -2312,20 +2374,23 @@
##
def liveProperties(self):
-
+
return super(DAVPrincipalResource, self).liveProperties() + (
(dav_namespace, "alternate-URI-set"),
- (dav_namespace, "principal-URL" ),
- (dav_namespace, "group-member-set" ),
- (dav_namespace, "group-membership" ),
+ (dav_namespace, "principal-URL"),
+ (dav_namespace, "group-member-set"),
+ (dav_namespace, "group-membership"),
)
+
def davComplianceClasses(self):
return ("1", "access-control",)
+
def isCollection(self):
return False
+
def readProperty(self, property, request):
def defer():
if type(property) is tuple:
@@ -2352,7 +2417,7 @@
element.HRef(p.principalURL())
for p in members
])
-
+
d = self.groupMembers()
d.addCallback(callback)
return d
@@ -2363,7 +2428,7 @@
element.HRef(g.principalURL())
for g in memberships
])
-
+
d = self.groupMemberships()
d.addCallback(callback)
return d
@@ -2383,6 +2448,7 @@
return maybeDeferred(defer)
+
##
# ACL
##
@@ -2397,6 +2463,7 @@
"""
return ()
+
def principalURL(self):
"""
See L{IDAVPrincipalResource.principalURL}.
@@ -2407,6 +2474,7 @@
"""
unimplemented(self)
+
def groupMembers(self):
"""
This implementation returns a Deferred which fires with C{()},
@@ -2418,6 +2486,7 @@
"""
return succeed(())
+
def expandedGroupMembers(self):
"""
This implementation returns a Deferred which fires with C{()},
@@ -2440,6 +2509,7 @@
"""
unimplemented(self)
+
def principalMatch(self, href):
"""
Check whether the supplied principal matches this principal or
@@ -2458,6 +2528,7 @@
)
return d
+
@inlineCallbacks
def containsPrincipal(self, principal):
"""
@@ -2472,6 +2543,7 @@
returnValue(principal in members)
+
class DAVPrincipalCollectionResource (DAVResource):
"""
WebDAV principal collection resource. (RFC 3744, section 5.8)
@@ -2512,9 +2584,11 @@
)
+
class AccessDeniedError(Exception):
+
def __init__(self, errors):
- """
+ """
An error to be raised when some request fails to meet
sufficient access privileges for a resource.
@@ -2529,6 +2603,8 @@
% (errors,))
self.errors = errors
+
+
##
# Utilities
##
@@ -2541,6 +2617,8 @@
else:
return True
+
+
class TwistedACLInheritable (WebDAVEmptyElement):
"""
When set on an ACE, this indicates that the ACE privileges should
Modified: CalendarServer/trunk/twistedcaldav/directory/wiki.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/wiki.py 2013-06-04 19:31:04 UTC (rev 11292)
+++ CalendarServer/trunk/twistedcaldav/directory/wiki.py 2013-06-04 20:52:00 UTC (rev 11293)
@@ -23,23 +23,25 @@
"WikiDirectoryService",
]
-from twisted.internet.defer import inlineCallbacks, returnValue
-from txdav.xml import element as davxml
-from twisted.web.xmlrpc import Proxy, Fault
-from twext.web2.http import HTTPError, StatusResponse
-from twext.web2.auth.wrapper import UnauthorizedResponse
-from twext.web2 import responsecode
+from calendarserver.platform.darwin.wiki import accessForUserToWiki
+from twext.internet.gaiendpoint import MultiFailure
from twext.python.log import Logger
+from twext.web2 import responsecode
+from twext.web2.auth.wrapper import UnauthorizedResponse
+from twext.web2.dav.resource import TwistedACLInheritable
+from twext.web2.http import HTTPError, StatusResponse
-from twext.web2.dav.resource import TwistedACLInheritable
-from twistedcaldav.config import config
-from twistedcaldav.directory.directory import (DirectoryService,
- DirectoryRecord,
- UnknownRecordTypeError)
-from calendarserver.platform.darwin.wiki import accessForUserToWiki
+from twisted.internet.defer import inlineCallbacks, returnValue
from twisted.web.error import Error as WebError
+from twisted.web.xmlrpc import Proxy, Fault
+from twistedcaldav.config import config
+from twistedcaldav.directory.directory import DirectoryService, \
+ DirectoryRecord, UnknownRecordTypeError
+
+from txdav.xml import element as davxml
+
log = Logger()
class WikiDirectoryService(DirectoryService):
@@ -58,31 +60,36 @@
def __repr__(self):
return "<%s %r>" % (self.__class__.__name__, self.realmName)
+
def __init__(self):
super(WikiDirectoryService, self).__init__()
self.byUID = {}
self.byShortName = {}
+
def recordTypes(self):
return (WikiDirectoryService.recordType_wikis,)
+
def listRecords(self, recordType):
return ()
+
def recordWithShortName(self, recordType, shortName):
if recordType != WikiDirectoryService.recordType_wikis:
raise UnknownRecordTypeError(recordType)
- if self.byShortName.has_key(shortName):
+ if shortName in self.byShortName:
record = self.byShortName[shortName]
return record
record = self._addRecord(shortName)
return record
+
def recordWithUID(self, uid):
- if self.byUID.has_key(uid):
+ if uid in self.byUID:
record = self.byUID[uid]
return record
@@ -93,6 +100,7 @@
else:
return None
+
def _addRecord(self, shortName):
record = WikiDirectoryRecord(
@@ -106,6 +114,7 @@
return record
+
class WikiDirectoryRecord(DirectoryRecord):
"""
L{DirectoryRecord} implementation for Wikis.
@@ -125,6 +134,7 @@
self.enabled = True
+
@inlineCallbacks
def getWikiAccess(userID, wikiID, method=None):
"""
@@ -189,6 +199,12 @@
(userID, wikiID, access))
returnValue(access)
+ except MultiFailure, e:
+ log.error("Wiki ACL error: user [%s], wiki [%s], MultiFailure [%s]" %
+ (userID, wikiID, e))
+ raise HTTPError(StatusResponse(responsecode.SERVICE_UNAVAILABLE,
+ "\n".join([str(f) for f in e.failures])))
+
except Fault, fault:
log.debug("Wiki ACL result: user [%s], wiki [%s], FAULT [%s]" % (userID,
@@ -202,8 +218,9 @@
raise HTTPError(StatusResponse(responsecode.NOT_FOUND,
fault.faultString))
- else: # Unknown fault returned from wiki server. Log the error and
- # return 503 Service Unavailable to the client.
+ else:
+ # Unknown fault returned from wiki server. Log the error and
+ # return 503 Service Unavailable to the client.
log.error("Wiki ACL error: user [%s], wiki [%s], FAULT [%s]" %
(userID, wikiID, fault))
raise HTTPError(StatusResponse(responsecode.SERVICE_UNAVAILABLE,
@@ -223,14 +240,16 @@
raise HTTPError(StatusResponse(responsecode.NOT_FOUND,
"Unknown Wiki"))
- else: # Unknown fault returned from wiki server. Log the error and
- # return 503 Service Unavailable to the client.
+ else:
+ # Unknown fault returned from wiki server. Log the error and
+ # return 503 Service Unavailable to the client.
log.error("Wiki ACL error: user [%s], wiki [%s], status [%s]" %
(userID, wikiID, status))
raise HTTPError(StatusResponse(responsecode.SERVICE_UNAVAILABLE,
w.message))
+
@inlineCallbacks
def getWikiACL(resource, request):
"""
@@ -271,55 +290,55 @@
# The ACL we returns has ACEs for the end-user and the wiki principal
# in case authzUser is the wiki principal.
if access == "read":
- request.wikiACL = davxml.ACL(
- davxml.ACE(
- request.authnUser,
- davxml.Grant(
- davxml.Privilege(davxml.Read()),
- davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
-
- # We allow write-properties so that direct sharees can change
- # e.g. calendar color properties
- davxml.Privilege(davxml.WriteProperties()),
- ),
- TwistedACLInheritable(),
- ),
- davxml.ACE(
- davxml.Principal(
- davxml.HRef.fromString("/principals/wikis/%s/" % (wikiID,))
- ),
- davxml.Grant(
- davxml.Privilege(davxml.Read()),
- davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
- ),
- TwistedACLInheritable(),
- )
- )
+ request.wikiACL = davxml.ACL(
+ davxml.ACE(
+ request.authnUser,
+ davxml.Grant(
+ davxml.Privilege(davxml.Read()),
+ davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
+
+ # We allow write-properties so that direct sharees can change
+ # e.g. calendar color properties
+ davxml.Privilege(davxml.WriteProperties()),
+ ),
+ TwistedACLInheritable(),
+ ),
+ davxml.ACE(
+ davxml.Principal(
+ davxml.HRef.fromString("/principals/wikis/%s/" % (wikiID,))
+ ),
+ davxml.Grant(
+ davxml.Privilege(davxml.Read()),
+ davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
+ ),
+ TwistedACLInheritable(),
+ )
+ )
returnValue(request.wikiACL)
elif access in ("write", "admin"):
- request.wikiACL = davxml.ACL(
- davxml.ACE(
- request.authnUser,
- davxml.Grant(
- davxml.Privilege(davxml.Read()),
- davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
- davxml.Privilege(davxml.Write()),
- ),
- TwistedACLInheritable(),
- ),
- davxml.ACE(
- davxml.Principal(
- davxml.HRef.fromString("/principals/wikis/%s/" % (wikiID,))
- ),
- davxml.Grant(
- davxml.Privilege(davxml.Read()),
- davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
- davxml.Privilege(davxml.Write()),
- ),
- TwistedACLInheritable(),
- )
- )
+ request.wikiACL = davxml.ACL(
+ davxml.ACE(
+ request.authnUser,
+ davxml.Grant(
+ davxml.Privilege(davxml.Read()),
+ davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
+ davxml.Privilege(davxml.Write()),
+ ),
+ TwistedACLInheritable(),
+ ),
+ davxml.ACE(
+ davxml.Principal(
+ davxml.HRef.fromString("/principals/wikis/%s/" % (wikiID,))
+ ),
+ davxml.Grant(
+ davxml.Privilege(davxml.Read()),
+ davxml.Privilege(davxml.ReadCurrentUserPrivilegeSet()),
+ davxml.Privilege(davxml.Write()),
+ ),
+ TwistedACLInheritable(),
+ )
+ )
returnValue(request.wikiACL)
else: # "no-access":
Modified: CalendarServer/trunk/twistedcaldav/method/propfind.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/propfind.py 2013-06-04 19:31:04 UTC (rev 11292)
+++ CalendarServer/trunk/twistedcaldav/method/propfind.py 2013-06-04 20:52:00 UTC (rev 11293)
@@ -135,14 +135,15 @@
if depth in ("1", "infinity") and noRoot:
resources = []
else:
- resources = [(True, self, my_url)]
+ resources = [(responsecode.OK, self, my_url)]
yield self.findChildrenFaster(
depth,
request,
- lambda x, y: resources.append((True, x, y)),
- lambda x, y: resources.append((False, x, y)),
+ lambda x, y: resources.append((responsecode.OK, x, y)),
+ lambda x, y: resources.append((responsecode.FORBIDDEN, x, y)),
None,
+ lambda x: resources.append((responsecode.SERVICE_UNAVAILABLE, None, x)),
None,
(davxml.Read(),),
inherited_aces=filtered_aces,
@@ -152,8 +153,8 @@
if depth == "1":
request.childCacheURIs = []
- for readable, resource, uri in resources:
- if readable:
+ for respcode, resource, uri in resources:
+ if respcode == responsecode.OK:
if search_properties is "names":
try:
resource_properties = (yield resource.listProperties(request))
@@ -225,7 +226,7 @@
elif hasattr(resource, "url"):
request.childCacheURIs.append(resource.url())
else:
- xml_response = davxml.StatusResponse(davxml.HRef(uri), davxml.Status.fromResponseCode(responsecode.FORBIDDEN))
+ xml_response = davxml.StatusResponse(davxml.HRef(uri), davxml.Status.fromResponseCode(respcode))
xml_responses.append(xml_response)
Modified: CalendarServer/trunk/twistedcaldav/method/report_addressbook_query.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/report_addressbook_query.py 2013-06-04 19:31:04 UTC (rev 11292)
+++ CalendarServer/trunk/twistedcaldav/method/report_addressbook_query.py 2013-06-04 20:52:00 UTC (rev 11293)
@@ -246,6 +246,7 @@
lambda x, y: ok_resources.append((x, y)),
None,
None,
+ None,
names,
(davxml.Read(),),
inherited_aces=filteredaces
Modified: CalendarServer/trunk/twistedcaldav/method/report_calendar_query.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/report_calendar_query.py 2013-06-04 19:31:04 UTC (rev 11292)
+++ CalendarServer/trunk/twistedcaldav/method/report_calendar_query.py 2013-06-04 20:52:00 UTC (rev 11293)
@@ -208,6 +208,7 @@
lambda x, y: ok_resources.append((x, y)),
None,
None,
+ None,
names,
(davxml.Read(),),
inherited_aces=filteredaces
Modified: CalendarServer/trunk/twistedcaldav/method/report_multiget_common.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/report_multiget_common.py 2013-06-04 19:31:04 UTC (rev 11292)
+++ CalendarServer/trunk/twistedcaldav/method/report_multiget_common.py 2013-06-04 20:52:00 UTC (rev 11293)
@@ -190,12 +190,14 @@
ok_resources = []
bad_resources = []
missing_resources = []
+ unavailable_resources = []
yield self.findChildrenFaster(
"1",
request,
lambda x, y: ok_resources.append((x, y)),
lambda x, y: bad_resources.append((x, y)),
lambda x: missing_resources.append(x),
+ lambda x: unavailable_resources.append(x),
valid_names,
(davxml.Read(),),
inherited_aces=filteredaces
@@ -231,9 +233,11 @@
for ignore_resource, href in bad_resources:
responses.append(davxml.StatusResponse(davxml.HRef.fromString(href), davxml.Status.fromResponseCode(responsecode.FORBIDDEN)))
- # Indicate error for all missing resources
+ # Indicate error for all missing/unavailable resources
for href in missing_resources:
responses.append(davxml.StatusResponse(davxml.HRef.fromString(href), davxml.Status.fromResponseCode(responsecode.NOT_FOUND)))
+ for href in unavailable_resources:
+ responses.append(davxml.StatusResponse(davxml.HRef.fromString(href), davxml.Status.fromResponseCode(responsecode.SERVICE_UNAVAILABLE)))
@inlineCallbacks
def doDirectoryAddressBookResponse():
Modified: CalendarServer/trunk/twistedcaldav/method/report_sync_collection.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/method/report_sync_collection.py 2013-06-04 19:31:04 UTC (rev 11292)
+++ CalendarServer/trunk/twistedcaldav/method/report_sync_collection.py 2013-06-04 20:52:00 UTC (rev 11293)
@@ -132,6 +132,7 @@
lambda x, y: ok_resources.append((x, y)),
lambda x, y: forbidden_resources.append((x, y)),
None,
+ None,
changed,
(element.Read(),),
inherited_aces=filteredaces
Modified: CalendarServer/trunk/twistedcaldav/resource.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/resource.py 2013-06-04 19:31:04 UTC (rev 11292)
+++ CalendarServer/trunk/twistedcaldav/resource.py 2013-06-04 20:52:00 UTC (rev 11293)
@@ -2124,7 +2124,7 @@
@inlineCallbacks
def findChildrenFaster(
- self, depth, request, okcallback, badcallback, missingcallback,
+ self, depth, request, okcallback, badcallback, missingcallback, unavailablecallback,
names, privileges, inherited_aces
):
"""
@@ -2135,7 +2135,7 @@
yield self._newStoreHome.loadChildren()
result = (yield super(CommonHomeResource, self).findChildrenFaster(
- depth, request, okcallback, badcallback, missingcallback, names, privileges, inherited_aces
+ depth, request, okcallback, badcallback, missingcallback, unavailablecallback, names, privileges, inherited_aces
))
returnValue(result)
Modified: CalendarServer/trunk/twistedcaldav/storebridge.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/storebridge.py 2013-06-04 19:31:04 UTC (rev 11292)
+++ CalendarServer/trunk/twistedcaldav/storebridge.py 2013-06-04 20:52:00 UTC (rev 11293)
@@ -379,7 +379,7 @@
@inlineCallbacks
def findChildrenFaster(
- self, depth, request, okcallback, badcallback, missingcallback,
+ self, depth, request, okcallback, badcallback, missingcallback, unavailablecallback,
names, privileges, inherited_aces
):
"""
@@ -393,7 +393,7 @@
yield self._newStoreObject.objectResources()
result = (yield super(_CommonHomeChildCollectionMixin, self).findChildrenFaster(
- depth, request, okcallback, badcallback, missingcallback, names, privileges, inherited_aces
+ depth, request, okcallback, badcallback, missingcallback, unavailablecallback, names, privileges, inherited_aces
))
returnValue(result)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130604/41bfd907/attachment-0001.html>
More information about the calendarserver-changes
mailing list