[CalendarServer-changes] [8964] CalendarServer/trunk
source_changes at macosforge.org
source_changes at macosforge.org
Mon Apr 2 16:59:16 PDT 2012
Revision: 8964
http://trac.macosforge.org/projects/calendarserver/changeset/8964
Author: sagen at apple.com
Date: 2012-04-02 16:59:16 -0700 (Mon, 02 Apr 2012)
Log Message:
-----------
Roll back r8963 since it's breaking unit tests
Revision Links:
--------------
http://trac.macosforge.org/projects/calendarserver/changeset/8963
Modified Paths:
--------------
CalendarServer/trunk/calendarserver/provision/root.py
CalendarServer/trunk/twext/web2/dav/method/report_principal_property_search.py
CalendarServer/trunk/twistedcaldav/extensions.py
Modified: CalendarServer/trunk/calendarserver/provision/root.py
===================================================================
--- CalendarServer/trunk/calendarserver/provision/root.py 2012-04-02 23:05:45 UTC (rev 8963)
+++ CalendarServer/trunk/calendarserver/provision/root.py 2012-04-02 23:59:16 UTC (rev 8964)
@@ -37,6 +37,7 @@
from twistedcaldav.cache import DisabledCache
from twistedcaldav.config import config
from twistedcaldav.extensions import DAVFile, CachingPropertyStore
+from twistedcaldav.extensions import DirectoryPrincipalPropertySearchMixIn
from twistedcaldav.extensions import ReadOnlyResourceMixIn
from twistedcaldav.resource import CalDAVComplianceMixIn
from twistedcaldav.resource import CalendarHomeResource, AddressBookHomeResource
@@ -48,7 +49,7 @@
log = Logger()
-class RootResource (ReadOnlyResourceMixIn, CalDAVComplianceMixIn, DAVFile):
+class RootResource (ReadOnlyResourceMixIn, DirectoryPrincipalPropertySearchMixIn, CalDAVComplianceMixIn, DAVFile):
"""
A special root resource that contains support checking SACLs
as well as adding responseFilters.
Modified: CalendarServer/trunk/twext/web2/dav/method/report_principal_property_search.py
===================================================================
--- CalendarServer/trunk/twext/web2/dav/method/report_principal_property_search.py 2012-04-02 23:05:45 UTC (rev 8963)
+++ CalendarServer/trunk/twext/web2/dav/method/report_principal_property_search.py 2012-04-02 23:59:16 UTC (rev 8964)
@@ -30,7 +30,6 @@
__all__ = ["report_DAV__principal_property_search"]
from twisted.internet.defer import deferredGenerator, waitForDeferred
-from twisted.internet.defer import inlineCallbacks, returnValue
from twext.python.log import Logger
from twext.web2 import responsecode
@@ -44,193 +43,14 @@
from twext.web2.dav.method.report import max_number_of_matches
from twext.web2.dav.resource import isPrincipalResource
-from twistedcaldav import customxml
-from twistedcaldav.customxml import calendarserver_namespace
-from twistedcaldav.config import config
-
log = Logger()
- at inlineCallbacks
-def report_DAV__principal_property_search(self, request,
- principal_property_search):
+def report_DAV__principal_property_search(self, request, principal_property_search):
"""
Generate a principal-property-search REPORT. (RFC 3744, section 9.4)
- Overrides twisted implementation, targeting only directory-enabled
- searching.
"""
- # Verify root element
- if not isinstance(principal_property_search, element.PrincipalPropertySearch):
- msg = "%s expected as root element, not %s." % (element.PrincipalPropertySearch.sname(), principal_property_search.sname())
- log.warn(msg)
- raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, msg))
- # Should we AND (the default) or OR (if test="anyof")?
- testMode = principal_property_search.attributes.get("test", "allof")
- if testMode not in ("allof", "anyof"):
- msg = "Bad XML: unknown value for test attribute: %s" % (testMode,)
- log.warn(msg)
- raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, msg))
- operand = "and" if testMode == "allof" else "or"
-
- # Are we narrowing results down to a single CUTYPE?
- cuType = principal_property_search.attributes.get("type", None)
- if cuType not in ("INDIVIDUAL", "GROUP", "RESOURCE", "ROOM", None):
- msg = "Bad XML: unknown value for type attribute: %s" % (cuType,)
- log.warn(msg)
- raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, msg))
-
- # Only handle Depth: 0
- depth = request.headers.getHeader("depth", "0")
- if depth != "0":
- log.err("Error in principal-property-search REPORT, Depth set to %s" % (depth,))
- raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, "Depth %s not allowed" % (depth,)))
-
- # Get any limit value from xml
- clientLimit = None
-
- # Get a single DAV:prop element from the REPORT request body
- propertiesForResource = None
- propElement = None
- propertySearches = []
- applyTo = False
- for child in principal_property_search.children:
- if child.qname() == (dav_namespace, "prop"):
- propertiesForResource = prop_common.propertyListForResource
- propElement = child
-
- elif child.qname() == (dav_namespace,
- "apply-to-principal-collection-set"):
- applyTo = True
-
- elif child.qname() == (dav_namespace, "property-search"):
- props = child.childOfType(element.PropertyContainer)
- props.removeWhitespaceNodes()
-
- match = child.childOfType(element.Match)
- caseless = match.attributes.get("caseless", "yes")
- if caseless not in ("yes", "no"):
- msg = "Bad XML: unknown value for caseless attribute: %s" % (caseless,)
- log.warn(msg)
- raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, msg))
- caseless = (caseless == "yes")
- matchType = match.attributes.get("match-type", u"contains").encode("utf-8")
- if matchType not in ("starts-with", "contains", "equals"):
- msg = "Bad XML: unknown value for match-type attribute: %s" % (matchType,)
- log.warn(msg)
- raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, msg))
-
- # Ignore any query strings under three letters
- matchText = str(match)
- if len(matchText) >= 3:
- propertySearches.append((props.children, matchText, caseless, matchType))
-
- elif child.qname() == (calendarserver_namespace, "limit"):
- try:
- nresults = child.childOfType(customxml.NResults)
- clientLimit = int(str(nresults))
- except (TypeError, ValueError,):
- msg = "Bad XML: unknown value for <limit> element"
- log.warn(msg)
- raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, msg))
-
- # Run report
- resultsWereLimited = None
- resources = []
- if applyTo or not hasattr(self, "directory"):
- for principalCollection in self.principalCollections():
- uri = principalCollection.principalCollectionURL()
- resource = (yield request.locateResource(uri))
- if resource:
- resources.append((resource, uri))
- else:
- resources.append((self, request.uri))
-
- # We need to access a directory service
- principalCollection = resources[0][0]
- if not hasattr(principalCollection, "directory"):
- # Use Twisted's implementation instead in this case
- result = orig_report_DAV__principal_property_search(self, request, principal_property_search)
- returnValue(result)
-
- dir = principalCollection.directory
-
- # See if we can take advantage of the directory
- fields = []
- nonDirectorySearches = []
- for props, match, caseless, matchType in propertySearches:
- nonDirectoryProps = []
- for prop in props:
- try:
- fieldName, match = principalCollection.propertyToField(
- prop, match)
- except ValueError, e:
- raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, str(e)))
- if fieldName:
- fields.append((fieldName, match, caseless, matchType))
- else:
- nonDirectoryProps.append(prop)
- if nonDirectoryProps:
- nonDirectorySearches.append((nonDirectoryProps, match,
- caseless, matchType))
-
- matchingResources = []
- matchcount = 0
-
- # nonDirectorySearches are ignored
- if fields:
-
- records = (yield dir.recordsMatchingFieldsWithCUType(fields,
- operand=operand, cuType=cuType))
-
- for record in records:
- resource = principalCollection.principalForRecord(record)
- if resource:
- matchingResources.append(resource)
-
- # We've determined this is a matching resource
- matchcount += 1
- if clientLimit is not None and matchcount >= clientLimit:
- resultsWereLimited = ("client", matchcount)
- break
- if matchcount >= config.MaxPrincipalSearchReportResults:
- resultsWereLimited = ("server", matchcount)
- break
-
- # Generate the response
- responses = []
- for resource in matchingResources:
- url = resource.url()
- yield prop_common.responseForHref(
- request,
- responses,
- element.HRef.fromString(url),
- resource,
- propertiesForResource,
- propElement
- )
-
- if resultsWereLimited is not None:
- if resultsWereLimited[0] == "server":
- log.err("Too many matching resources in "
- "principal-property-search report")
- responses.append(element.StatusResponse(
- element.HRef.fromString(request.uri),
- element.Status.fromResponseCode(
- responsecode.INSUFFICIENT_STORAGE_SPACE
- ),
- element.Error(element.NumberOfMatchesWithinLimits()),
- element.ResponseDescription("Results limited by %s at %d"
- % resultsWereLimited),
- ))
- returnValue(MultiStatusResponse(responses))
-
- at deferredGenerator
-def orig_report_DAV__principal_property_search(self, request, principal_property_search):
- """
- Generate a principal-property-search REPORT. (RFC 3744, section 9.4)
- """
-
# Verify root element
if not isinstance(principal_property_search, element.PrincipalPropertySearch):
raise ValueError("%s expected as root element, not %s."
@@ -278,7 +98,6 @@
else:
return False
- @deferredGenerator
def propertySearch(resource, request):
"""
Test the resource to see if it contains properties matching the
@@ -304,6 +123,8 @@
yield True
+ propertySearch = deferredGenerator(propertySearch)
+
# Run report
try:
resources = []
@@ -366,3 +187,5 @@
))
yield MultiStatusResponse(responses)
+
+report_DAV__principal_property_search = deferredGenerator(report_DAV__principal_property_search)
Modified: CalendarServer/trunk/twistedcaldav/extensions.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/extensions.py 2012-04-02 23:05:45 UTC (rev 8963)
+++ CalendarServer/trunk/twistedcaldav/extensions.py 2012-04-02 23:59:16 UTC (rev 8964)
@@ -35,11 +35,13 @@
from twisted.internet.defer import succeed, maybeDeferred
from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.cred.error import LoginFailed, UnauthorizedLogin
from twisted.web.template import Element, XMLFile, renderer, tags, flattenString
from twisted.python.modules import getModule
from twext.web2 import responsecode, server
+from twext.web2.auth.wrapper import UnauthorizedResponse
from twext.web2.http import HTTPError, Response, RedirectResponse
from twext.web2.http import StatusResponse
from twext.web2.http_headers import MimeType
@@ -47,26 +49,211 @@
from twext.web2.static import MetaDataMixin, StaticRenderMixin
from txdav.xml import element
from txdav.xml.element import dav_namespace
+from twext.web2.dav.auth import PrincipalCredentials
+from twext.web2.dav.http import MultiStatusResponse
+from twext.web2.dav.idav import IDAVPrincipalResource
from twext.web2.dav.static import DAVFile as SuperDAVFile
from twext.web2.dav.resource import DAVResource as SuperDAVResource
from twext.web2.dav.resource import (
DAVPrincipalResource as SuperDAVPrincipalResource
)
from twisted.internet.defer import gatherResults
+from twext.web2.dav.method import prop_common
from twext.python.log import Logger, LoggingMixIn
from twistedcaldav import customxml
from twistedcaldav.customxml import calendarserver_namespace
+from twistedcaldav.directory.directory import DirectoryService
from twistedcaldav.method.report import http_REPORT
+from twistedcaldav.config import config
+
thisModule = getModule(__name__)
log = Logger()
+class DirectoryPrincipalPropertySearchMixIn(object):
+
+ @inlineCallbacks
+ def report_DAV__principal_property_search(self, request,
+ principal_property_search):
+ """
+ Generate a principal-property-search REPORT. (RFC 3744, section 9.4)
+ Overrides twisted implementation, targeting only directory-enabled
+ searching.
+ """
+ # Verify root element
+ if not isinstance(principal_property_search, element.PrincipalPropertySearch):
+ msg = "%s expected as root element, not %s." % (element.PrincipalPropertySearch.sname(), principal_property_search.sname())
+ log.warn(msg)
+ raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, msg))
+
+ # Should we AND (the default) or OR (if test="anyof")?
+ testMode = principal_property_search.attributes.get("test", "allof")
+ if testMode not in ("allof", "anyof"):
+ msg = "Bad XML: unknown value for test attribute: %s" % (testMode,)
+ log.warn(msg)
+ raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, msg))
+ operand = "and" if testMode == "allof" else "or"
+
+ # Are we narrowing results down to a single CUTYPE?
+ cuType = principal_property_search.attributes.get("type", None)
+ if cuType not in ("INDIVIDUAL", "GROUP", "RESOURCE", "ROOM", None):
+ msg = "Bad XML: unknown value for type attribute: %s" % (cuType,)
+ log.warn(msg)
+ raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, msg))
+
+ # Only handle Depth: 0
+ depth = request.headers.getHeader("depth", "0")
+ if depth != "0":
+ log.err("Error in principal-property-search REPORT, Depth set to %s" % (depth,))
+ raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, "Depth %s not allowed" % (depth,)))
+
+ # Get any limit value from xml
+ clientLimit = None
+
+ # Get a single DAV:prop element from the REPORT request body
+ propertiesForResource = None
+ propElement = None
+ propertySearches = []
+ applyTo = False
+ for child in principal_property_search.children:
+ if child.qname() == (dav_namespace, "prop"):
+ propertiesForResource = prop_common.propertyListForResource
+ propElement = child
+
+ elif child.qname() == (dav_namespace,
+ "apply-to-principal-collection-set"):
+ applyTo = True
+
+ elif child.qname() == (dav_namespace, "property-search"):
+ props = child.childOfType(element.PropertyContainer)
+ props.removeWhitespaceNodes()
+
+ match = child.childOfType(element.Match)
+ caseless = match.attributes.get("caseless", "yes")
+ if caseless not in ("yes", "no"):
+ msg = "Bad XML: unknown value for caseless attribute: %s" % (caseless,)
+ log.warn(msg)
+ raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, msg))
+ caseless = (caseless == "yes")
+ matchType = match.attributes.get("match-type", u"contains").encode("utf-8")
+ if matchType not in ("starts-with", "contains", "equals"):
+ msg = "Bad XML: unknown value for match-type attribute: %s" % (matchType,)
+ log.warn(msg)
+ raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, msg))
+
+ # Ignore any query strings under three letters
+ matchText = str(match)
+ if len(matchText) >= 3:
+ propertySearches.append((props.children, matchText, caseless, matchType))
+
+ elif child.qname() == (calendarserver_namespace, "limit"):
+ try:
+ nresults = child.childOfType(customxml.NResults)
+ clientLimit = int(str(nresults))
+ except (TypeError, ValueError,):
+ msg = "Bad XML: unknown value for <limit> element"
+ log.warn(msg)
+ raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, msg))
+
+ # Run report
+ resultsWereLimited = None
+ resources = []
+ if applyTo or not hasattr(self, "directory"):
+ for principalCollection in self.principalCollections():
+ uri = principalCollection.principalCollectionURL()
+ resource = (yield request.locateResource(uri))
+ if resource:
+ resources.append((resource, uri))
+ else:
+ resources.append((self, request.uri))
+
+ # We need to access a directory service
+ principalCollection = resources[0][0]
+ if not hasattr(principalCollection, "directory"):
+ # Use Twisted's implementation instead in this case
+ result = (yield super(DirectoryPrincipalPropertySearchMixIn, self).report_DAV__principal_property_search(request, principal_property_search))
+ returnValue(result)
+
+ dir = principalCollection.directory
+
+ # See if we can take advantage of the directory
+ fields = []
+ nonDirectorySearches = []
+ for props, match, caseless, matchType in propertySearches:
+ nonDirectoryProps = []
+ for prop in props:
+ try:
+ fieldName, match = principalCollection.propertyToField(
+ prop, match)
+ except ValueError, e:
+ raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, str(e)))
+ if fieldName:
+ fields.append((fieldName, match, caseless, matchType))
+ else:
+ nonDirectoryProps.append(prop)
+ if nonDirectoryProps:
+ nonDirectorySearches.append((nonDirectoryProps, match,
+ caseless, matchType))
+
+ matchingResources = []
+ matchcount = 0
+
+ # nonDirectorySearches are ignored
+ if fields:
+
+ records = (yield dir.recordsMatchingFieldsWithCUType(fields,
+ operand=operand, cuType=cuType))
+
+ for record in records:
+ resource = principalCollection.principalForRecord(record)
+ if resource:
+ matchingResources.append(resource)
+
+ # We've determined this is a matching resource
+ matchcount += 1
+ if clientLimit is not None and matchcount >= clientLimit:
+ resultsWereLimited = ("client", matchcount)
+ break
+ if matchcount >= config.MaxPrincipalSearchReportResults:
+ resultsWereLimited = ("server", matchcount)
+ break
+
+ # Generate the response
+ responses = []
+ for resource in matchingResources:
+ url = resource.url()
+ yield prop_common.responseForHref(
+ request,
+ responses,
+ element.HRef.fromString(url),
+ resource,
+ propertiesForResource,
+ propElement
+ )
+
+ if resultsWereLimited is not None:
+ if resultsWereLimited[0] == "server":
+ log.err("Too many matching resources in "
+ "principal-property-search report")
+ responses.append(element.StatusResponse(
+ element.HRef.fromString(request.uri),
+ element.Status.fromResponseCode(
+ responsecode.INSUFFICIENT_STORAGE_SPACE
+ ),
+ element.Error(element.NumberOfMatchesWithinLimits()),
+ element.ResponseDescription("Results limited by %s at %d"
+ % resultsWereLimited),
+ ))
+ returnValue(MultiStatusResponse(responses))
+
+
+
class DirectoryElement(Element):
"""
A L{DirectoryElement} is an L{Element} for rendering the contents of a
@@ -282,7 +469,8 @@
return wrapper
-class DAVResource (SuperDAVResource, LoggingMixIn,
+class DAVResource (DirectoryPrincipalPropertySearchMixIn,
+ SuperDAVResource, LoggingMixIn,
DirectoryRenderingMixIn, StaticRenderMixin):
"""
Extended L{twext.web2.dav.resource.DAVResource} implementation.
@@ -409,7 +597,8 @@
-class DAVPrincipalResource (SuperDAVPrincipalResource, LoggingMixIn,
+class DAVPrincipalResource (DirectoryPrincipalPropertySearchMixIn,
+ SuperDAVPrincipalResource, LoggingMixIn,
DirectoryRenderingMixIn):
"""
Extended L{twext.web2.dav.static.DAVFile} implementation.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20120402/39b9ce5a/attachment-0001.html>
More information about the calendarserver-changes
mailing list