def _namedPropertiesForResource(request, props, resource, forbidden=False): """ Return the specified properties on the specified resource. @param request: the L{IRequest} for the current request. @param props: a list of property elements or qname tuples for the properties of interest. @param resource: the L{DAVResource} for the targeted resource. @return: a map of OK and NOT FOUND property values. """ properties_by_status = { responsecode.OK: [], responsecode.FORBIDDEN: [], responsecode.NOT_FOUND: [], } for property in props: if isinstance(property, element.WebDAVElement): qname = property.qname() else: qname = property if forbidden: properties_by_status[responsecode.FORBIDDEN].append( propertyName(qname)) else: props = (yield resource.listProperties(request)) if qname in props: try: prop = (yield resource.readProperty(qname, request)) properties_by_status[responsecode.OK].append(prop) except: f = Failure() log.error( "Error reading property %r for resource %s: %s" % (qname, request.uri, f.value)) status = statusForFailure( f, "getting property: %s" % (qname, )) if status not in properties_by_status: properties_by_status[status] = [] properties_by_status[status].append( propertyName(qname)) else: properties_by_status[responsecode.NOT_FOUND].append( propertyName(qname)) returnValue(properties_by_status)
def _namedPropertiesForResource(request, props, resource): """ Return the specified properties on the specified resource. @param request: the L{IRequest} for the current request. @param props: a list of property elements or qname tuples for the properties of interest. @param resource: the L{DAVFile} for the targetted resource. @return: a map of OK and NOT FOUND property values. """ properties_by_status = { responsecode.OK : [], responsecode.NOT_FOUND : [], } for property in props: if isinstance(property, element.WebDAVElement): qname = property.qname() else: qname = property props = waitForDeferred(resource.listProperties(request)) yield props props = props.getResult() if qname in props: try: prop = waitForDeferred(resource.readProperty(qname, request)) yield prop prop = prop.getResult() properties_by_status[responsecode.OK].append(prop) except: f = Failure() status = statusForFailure(f, "getting property: %s" % (qname,)) if status != responsecode.NOT_FOUND: log.error("Error reading property %r for resource %s: %s" % (qname, request.uri, f.value)) if status not in properties_by_status: properties_by_status[status] = [] properties_by_status[status].append(propertyName(qname)) else: properties_by_status[responsecode.NOT_FOUND].append(propertyName(qname)) yield properties_by_status
def _namedPropertiesForResource(request, props, resource, calendar=None, timezone=None, vcard=None, isowner=True, dataAllowed=True, forbidden=False): """ Return the specified properties on the specified resource. @param request: the L{IRequest} for the current request. @param props: a list of property elements or qname tuples for the properties of interest. @param resource: the L{CalDAVResource} for the targeted resource. @param calendar: the L{Component} for the calendar for the resource. This may be None if the calendar has not already been read in, in which case the resource will be used to get the calendar if needed. @param timezone: the L{Component} the VTIMEZONE to use for floating/all-day. @param vcard: the L{Component} for the vcard for the resource. This may be None if the vcard has not already been read in, in which case the resource will be used to get the vcard if needed. @param isowner: C{True} if the authorized principal making the request is the DAV:owner, C{False} otherwise. @param dataAllowed: C{True} if calendar/address data is allowed to be returned, C{False} otherwise. @param forbidden: if C{True} then return 403 status for all properties, C{False} otherwise. @return: a map of OK and NOT FOUND property values. """ properties_by_status = { responsecode.OK : [], responsecode.FORBIDDEN : [], responsecode.NOT_FOUND : [], } # Look for Prefer header first, then try Brief prefer = request.headers.getHeader("prefer", {}) returnMinimal = any([key == "return" and value == "minimal" for key, value, _ignore_args in prefer]) if not returnMinimal: returnMinimal = request.headers.getHeader("brief", False) for property in props: if isinstance(property, element.WebDAVElement): qname = property.qname() else: qname = property if forbidden: properties_by_status[responsecode.FORBIDDEN].append(propertyName(qname)) continue if isinstance(property, caldavxml.CalendarData): if dataAllowed: # Handle private events access restrictions if calendar is None: calendar = (yield resource.componentForUser()) filtered = HiddenInstanceFilter().filter(calendar) filtered = PrivateEventFilter(resource.accessMode, isowner).filter(filtered) filtered = CalendarDataFilter(property, timezone).filter(filtered) propvalue = CalendarData.fromCalendar(filtered, format=property.content_type) properties_by_status[responsecode.OK].append(propvalue) else: properties_by_status[responsecode.FORBIDDEN].append(propertyName(qname)) continue if isinstance(property, carddavxml.AddressData): if dataAllowed: if vcard is None: vcard = (yield resource.vCard()) filtered = AddressDataFilter(property).filter(vcard) propvalue = AddressData.fromAddress(filtered, format=property.content_type) properties_by_status[responsecode.OK].append(propvalue) else: properties_by_status[responsecode.FORBIDDEN].append(propertyName(qname)) continue has = (yield resource.hasProperty(property, request)) if has: try: prop = (yield resource.readProperty(property, request)) if prop is not None: properties_by_status[responsecode.OK].append(prop) elif not returnMinimal: properties_by_status[responsecode.NOT_FOUND].append(propertyName(qname)) except HTTPError: f = Failure() status = statusForFailure(f, "getting property: %s" % (qname,)) if status not in properties_by_status: properties_by_status[status] = [] if not returnMinimal or status != responsecode.NOT_FOUND: properties_by_status[status].append(propertyName(qname)) elif not returnMinimal: properties_by_status[responsecode.NOT_FOUND].append(propertyName(qname)) returnValue(properties_by_status)
def _defer(props): properties_by_status = { responsecode.OK: [propertyName(p) for p in props] } return properties_by_status
def _namedPropertiesForResource(request, props, resource, calendar=None, timezone=None, vcard=None, isowner=True): """ Return the specified properties on the specified resource. @param request: the L{IRequest} for the current request. @param props: a list of property elements or qname tuples for the properties of interest. @param resource: the L{CalDAVResource} for the targeted resource. @param calendar: the L{Component} for the calendar for the resource. This may be None if the calendar has not already been read in, in which case the resource will be used to get the calendar if needed. @param timezone: the L{Component} the VTIMEZONE to use for floating/all-day. @param vcard: the L{Component} for the vcard for the resource. This may be None if the vcard has not already been read in, in which case the resource will be used to get the vcard if needed. @param isowner: C{True} if the authorized principal making the request is the DAV:owner, C{False} otherwise. @return: a map of OK and NOT FOUND property values. """ properties_by_status = { responsecode.OK : [], responsecode.NOT_FOUND : [], } # Look for Prefer header first, then try Brief prefer = request.headers.getHeader("prefer", {}) returnMinimal = any([key == "return" and value == "minimal" for key, value, _ignore_args in prefer]) if not returnMinimal: returnMinimal = request.headers.getHeader("brief", False) for property in props: if isinstance(property, caldavxml.CalendarData): # Handle private events access restrictions if calendar is None: calendar = (yield resource.iCalendarForUser(request)) filtered = HiddenInstanceFilter().filter(calendar) filtered = PrivateEventFilter(resource.accessMode, isowner).filter(filtered) filtered = CalendarDataFilter(property, timezone).filter(filtered) propvalue = CalendarData.fromCalendar(filtered, format=property.content_type) properties_by_status[responsecode.OK].append(propvalue) continue if isinstance(property, carddavxml.AddressData): if vcard is None: vcard = (yield resource.vCard()) filtered = AddressDataFilter(property).filter(vcard) propvalue = AddressData.fromAddress(filtered, format=property.content_type) properties_by_status[responsecode.OK].append(propvalue) continue if isinstance(property, element.WebDAVElement): qname = property.qname() else: qname = property has = (yield resource.hasProperty(property, request)) if has: try: prop = (yield resource.readProperty(property, request)) if prop is not None: properties_by_status[responsecode.OK].append(prop) elif not returnMinimal: properties_by_status[responsecode.NOT_FOUND].append(propertyName(qname)) except HTTPError: f = Failure() status = statusForFailure(f, "getting property: %s" % (qname,)) if status not in properties_by_status: properties_by_status[status] = [] if not returnMinimal or status != responsecode.NOT_FOUND: properties_by_status[status].append(propertyName(qname)) elif not returnMinimal: properties_by_status[responsecode.NOT_FOUND].append(propertyName(qname)) returnValue(properties_by_status)
def report_DAV__expand_property(self, request, expand_property): """ Generate an expand-property REPORT. (RFC 3253, section 3.8) TODO: for simplicity we will only support one level of expansion. """ # Verify root element if not isinstance(expand_property, element.ExpandProperty): raise ValueError( "%s expected as root element, not %s." % (element.ExpandProperty.sname(), expand_property.sname())) # Only handle Depth: 0 depth = request.headers.getHeader("depth", "0") if depth != "0": log.error("Non-zero depth is not allowed: %s" % (depth, )) raise HTTPError( StatusResponse(responsecode.BAD_REQUEST, "Depth %s not allowed" % (depth, ))) # # Get top level properties to expand and make sure we only have one level # properties = {} for property in expand_property.children: namespace = property.attributes.get("namespace", dav_namespace) name = property.attributes.get("name", "") # Make sure children have no children props_to_find = [] for child in property.children: if child.children: log.error( "expand-property REPORT only supports single level expansion" ) raise HTTPError( StatusResponse( responsecode.NOT_IMPLEMENTED, "expand-property REPORT only supports single level expansion" )) child_namespace = child.attributes.get("namespace", dav_namespace) child_name = child.attributes.get("name", "") props_to_find.append((child_namespace, child_name)) properties[(namespace, name)] = props_to_find # # Generate the expanded responses status for each top-level property # properties_by_status = { responsecode.OK: [], responsecode.NOT_FOUND: [], } filteredaces = None lastParent = None for qname in properties.iterkeys(): try: prop = (yield self.readProperty(qname, request)) # Form the PROPFIND-style DAV:prop element we need later props_to_return = element.PropertyContainer(*properties[qname]) # Now dereference any HRefs responses = [] for href in prop.children: if isinstance(href, element.HRef): # Locate the Href resource and its parent resource_uri = str(href) child = (yield request.locateResource(resource_uri)) if not child or not child.exists(): responses.append( element.StatusResponse( href, element.Status.fromResponseCode( responsecode.NOT_FOUND))) continue parent = (yield request.locateResource( parentForURL(resource_uri))) # Check privileges on parent - must have at least DAV:read try: yield parent.checkPrivileges(request, (element.Read(), )) except AccessDeniedError: responses.append( element.StatusResponse( href, element.Status.fromResponseCode( responsecode.FORBIDDEN))) continue # Cache the last parent's inherited aces for checkPrivileges optimization if lastParent != parent: lastParent = parent # Do some optimisation of access control calculation by determining any inherited ACLs outside of # the child resource loop and supply those to the checkPrivileges on each child. filteredaces = ( yield parent.inheritedACEsforChildren(request)) # Check privileges - must have at least DAV:read try: yield child.checkPrivileges( request, (element.Read(), ), inherited_aces=filteredaces) except AccessDeniedError: responses.append( element.StatusResponse( href, element.Status.fromResponseCode( responsecode.FORBIDDEN))) continue # Now retrieve all the requested properties on the HRef resource yield prop_common.responseForHref( request, responses, href, child, prop_common.propertyListForResource, props_to_return, ) prop.children = responses properties_by_status[responsecode.OK].append(prop) except: f = Failure() log.error( "Error reading property {qname} for resource {req}: {failure}", qname=qname, req=request.uri, failure=f.value) status = statusForFailure(f, "getting property: %s" % (qname, )) if status not in properties_by_status: properties_by_status[status] = [] properties_by_status[status].append(propertyName(qname)) # Build the overall response propstats = [ element.PropertyStatus( element.PropertyContainer(*properties_by_status[pstatus]), element.Status.fromResponseCode(pstatus)) for pstatus in properties_by_status if properties_by_status[pstatus] ] returnValue( MultiStatusResponse( (element.PropertyStatusResponse(element.HRef(request.uri), *propstats), )))
def report_DAV__expand_property(self, request, expand_property): """ Generate an expand-property REPORT. (RFC 3253, section 3.8) TODO: for simplicity we will only support one level of expansion. """ # Verify root element if not isinstance(expand_property, element.ExpandProperty): raise ValueError("%s expected as root element, not %s." % (element.ExpandProperty.sname(), expand_property.sname())) # Only handle Depth: 0 depth = request.headers.getHeader("depth", "0") if depth != "0": log.error("Non-zero depth is not allowed: %s" % (depth,)) raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, "Depth %s not allowed" % (depth,))) # # Get top level properties to expand and make sure we only have one level # properties = {} for property in expand_property.children: namespace = property.attributes.get("namespace", dav_namespace) name = property.attributes.get("name", "") # Make sure children have no children props_to_find = [] for child in property.children: if child.children: log.error("expand-property REPORT only supports single level expansion") raise HTTPError(StatusResponse( responsecode.NOT_IMPLEMENTED, "expand-property REPORT only supports single level expansion" )) child_namespace = child.attributes.get("namespace", dav_namespace) child_name = child.attributes.get("name", "") props_to_find.append((child_namespace, child_name)) properties[(namespace, name)] = props_to_find # # Generate the expanded responses status for each top-level property # properties_by_status = { responsecode.OK : [], responsecode.NOT_FOUND : [], } filteredaces = None lastParent = None for qname in properties.iterkeys(): try: prop = (yield self.readProperty(qname, request)) # Form the PROPFIND-style DAV:prop element we need later props_to_return = element.PropertyContainer(*properties[qname]) # Now dereference any HRefs responses = [] for href in prop.children: if isinstance(href, element.HRef): # Locate the Href resource and its parent resource_uri = str(href) child = (yield request.locateResource(resource_uri)) if not child or not child.exists(): responses.append(element.StatusResponse(href, element.Status.fromResponseCode(responsecode.NOT_FOUND))) continue parent = (yield request.locateResource(parentForURL(resource_uri))) # Check privileges on parent - must have at least DAV:read try: yield parent.checkPrivileges(request, (element.Read(),)) except AccessDeniedError: responses.append(element.StatusResponse(href, element.Status.fromResponseCode(responsecode.FORBIDDEN))) continue # Cache the last parent's inherited aces for checkPrivileges optimization if lastParent != parent: lastParent = parent # Do some optimisation of access control calculation by determining any inherited ACLs outside of # the child resource loop and supply those to the checkPrivileges on each child. filteredaces = (yield parent.inheritedACEsforChildren(request)) # Check privileges - must have at least DAV:read try: yield child.checkPrivileges(request, (element.Read(),), inherited_aces=filteredaces) except AccessDeniedError: responses.append(element.StatusResponse(href, element.Status.fromResponseCode(responsecode.FORBIDDEN))) continue # Now retrieve all the requested properties on the HRef resource yield prop_common.responseForHref( request, responses, href, child, prop_common.propertyListForResource, props_to_return, ) prop.children = responses properties_by_status[responsecode.OK].append(prop) except: f = Failure() log.error( "Error reading property {qname} for resource {req}: {failure}", qname=qname, req=request.uri, failure=f.value ) status = statusForFailure(f, "getting property: %s" % (qname,)) if status not in properties_by_status: properties_by_status[status] = [] properties_by_status[status].append(propertyName(qname)) # Build the overall response propstats = [ element.PropertyStatus( element.PropertyContainer(*properties_by_status[pstatus]), element.Status.fromResponseCode(pstatus) ) for pstatus in properties_by_status if properties_by_status[pstatus] ] returnValue(MultiStatusResponse((element.PropertyStatusResponse(element.HRef(request.uri), *propstats),)))