Пример #1
0
        request.submethod = name

    try:
        method = getattr(self, method_name)

        # Also double-check via supported-reports property
        reports = self.supportedReports()
        test = lookupElement((namespace, name))
        if not test:
            raise AttributeError()
        test = davxml.Report(test())
        if test not in reports:
            raise AttributeError()
    except AttributeError:
        #
        # Requested report is not supported.
        #
        log.error("Unsupported REPORT %s for resource %s (no method %s)" %
                  (encodeXMLName(namespace, name), self, method_name))

        raise HTTPError(
            ErrorResponse(responsecode.FORBIDDEN, davxml.SupportedReport()))

    d = waitForDeferred(method(request, doc.root_element))
    yield d
    yield d.getResult()


http_REPORT = deferredGenerator(http_REPORT)
Пример #2
0
        reports = self.supportedReports()
        test = lookupElement((namespace, name))
        if not test:
            raise AttributeError()
        test = davxml.Report(test())
        if test not in reports:
            raise AttributeError()
    except AttributeError:
        #
        # Requested report is not supported.
        #
        log.error("Unsupported REPORT {name} for resource {resource} (no method {method})",
                  name=encodeXMLName(namespace, name), resource=self, method=method_name)

        raise HTTPError(ErrorResponse(
            responsecode.FORBIDDEN,
            davxml.SupportedReport(),
            "Report not supported on this resource",
        ))

    #
    # Check authentication and access controls
    #
    privileges = (davxml.Read(),)
    if method_name == "report_urn_ietf_params_xml_ns_caldav_free_busy_query":
        privileges = (caldavxml.ReadFreeBusy(),)
    yield self.authorize(request, privileges)

    result = (yield method(request, doc.root_element))
    returnValue(result)
def report_DAV__sync_collection(self, request, sync_collection):
    """
    Generate a sync-collection REPORT.
    """

    # These resource support the report
    if not config.EnableSyncReport or element.Report(element.SyncCollection(),) not in self.supportedReports():
        log.error("sync-collection report is only allowed on calendar/inbox/addressbook/notification collection resources {s!r}", s=self)
        raise HTTPError(ErrorResponse(
            responsecode.FORBIDDEN,
            element.SupportedReport(),
            "Report not supported on this resource",
        ))

    responses = []

    # Do not support limit
    if sync_collection.sync_limit is not None:
        raise HTTPError(ErrorResponse(
            responsecode.INSUFFICIENT_STORAGE_SPACE,
            element.NumberOfMatchesWithinLimits(),
            "Report limit not supported",
        ))

    # Process Depth and sync-level for backwards compatibility
    # Use sync-level if present and ignore Depth, else use Depth
    if sync_collection.sync_level:
        depth = sync_collection.sync_level
        if depth == "infinite":
            depth = "infinity"
        descriptor = "DAV:sync-level"
    else:
        depth = request.headers.getHeader("depth", None)
        descriptor = "Depth header without DAV:sync-level"

    if depth not in ("1", "infinity"):
        log.error("sync-collection report with invalid depth header: {d}", d=depth)
        raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, "Invalid %s value" % (descriptor,)))

    propertyreq = sync_collection.property.children if sync_collection.property else None

    # Do some optimization 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 self.inheritedACEsforChildren(request))

    changed, removed, notallowed, newtoken, resourceChanged = yield self.whatchanged(sync_collection.sync_token, depth)

    # Now determine which valid resources are readable and which are not
    ok_resources = []
    forbidden_resources = []
    if changed:
        yield self.findChildrenFaster(
            depth,
            request,
            lambda x, y: ok_resources.append((x, y)),
            lambda x, y: forbidden_resources.append((x, y)),
            None,
            None,
            changed,
            (element.Read(),),
            inherited_aces=filteredaces
        )

    if resourceChanged:
        ok_resources.append((self, request.uri))

    for child, child_uri in ok_resources:
        href = element.HRef.fromString(child_uri)
        try:
            if propertyreq:
                yield responseForHref(
                    request,
                    responses,
                    href,
                    child,
                    functools.partial(_namedPropertiesForResource, dataAllowed=False, forbidden=False),
                    propertyreq)
            else:
                responses.append(element.StatusResponse(element.HRef.fromString(href), element.Status.fromResponseCode(responsecode.OK)))
        except ConcurrentModification:
            # This can happen because of a race-condition between the
            # time we determine which resources exist and the deletion
            # of one of these resources in another request.  In this
            # case, we ignore the now missing resource rather
            # than raise an error for the entire report.
            log.error("Missing resource during sync: {h}", h=href)

    for child, child_uri in forbidden_resources:
        href = element.HRef.fromString(child_uri)
        try:
            if propertyreq:
                yield responseForHref(
                    request,
                    responses,
                    href,
                    child,
                    functools.partial(_namedPropertiesForResource, dataAllowed=False, forbidden=True),
                    propertyreq)
            else:
                responses.append(element.StatusResponse(element.HRef.fromString(href), element.Status.fromResponseCode(responsecode.OK)))
        except ConcurrentModification:
            # This can happen because of a race-condition between the
            # time we determine which resources exist and the deletion
            # of one of these resources in another request.  In this
            # case, we ignore the now missing resource rather
            # than raise an error for the entire report.
            log.error("Missing resource during sync: {h}", h=href)

    for name in removed:
        href = element.HRef.fromString(joinURL(request.uri, name))
        responses.append(element.StatusResponse(element.HRef.fromString(href), element.Status.fromResponseCode(responsecode.NOT_FOUND)))

    for name in notallowed:
        href = element.HRef.fromString(joinURL(request.uri, name))
        responses.append(element.StatusResponse(element.HRef.fromString(href), element.Status.fromResponseCode(responsecode.NOT_ALLOWED)))

    if not hasattr(request, "extendedLogItems"):
        request.extendedLogItems = {}
    request.extendedLogItems["responses"] = len(responses)

    responses.append(element.SyncToken.fromString(newtoken))

    returnValue(MultiStatusResponse(responses))
def report_DAV__sync_collection(self, request, sync_collection):
    """
    Generate a sync-collection REPORT.
    """

    # These resource support the report
    if not config.EnableSyncReport or element.Report(
            element.SyncCollection(), ) not in self.supportedReports():
        log.error(
            "sync-collection report is only allowed on calendar/inbox/addressbook/notification collection resources %s"
            % (self, ))
        raise HTTPError(
            ErrorResponse(
                responsecode.FORBIDDEN,
                element.SupportedReport(),
                "Report not supported on this resource",
            ))

    responses = []

    # Do not support limit
    if sync_collection.sync_limit is not None:
        raise HTTPError(
            ErrorResponse(
                responsecode.INSUFFICIENT_STORAGE_SPACE,
                element.NumberOfMatchesWithinLimits(),
                "Report limit not supported",
            ))

    # Process Depth and sync-level for backwards compatibility
    # Use sync-level if present and ignore Depth, else use Depth
    if sync_collection.sync_level:
        depth = sync_collection.sync_level
        if depth == "infinite":
            depth = "infinity"
        descriptor = "DAV:sync-level"
    else:
        depth = request.headers.getHeader("depth", None)
        descriptor = "Depth header without DAV:sync-level"

    if depth not in ("1", "infinity"):
        log.error("sync-collection report with invalid depth header: %s" %
                  (depth, ))
        raise HTTPError(
            StatusResponse(responsecode.BAD_REQUEST,
                           "Invalid %s value" % (descriptor, )))

    propertyreq = sync_collection.property.children if sync_collection.property else None

    @inlineCallbacks
    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)

    # Do some optimization 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 self.inheritedACEsforChildren(request))

    changed, removed, notallowed, newtoken = yield self.whatchanged(
        sync_collection.sync_token, depth)

    # Now determine which valid resources are readable and which are not
    ok_resources = []
    forbidden_resources = []
    if changed:
        yield self.findChildrenFaster(depth,
                                      request,
                                      lambda x, y: ok_resources.append((x, y)),
                                      lambda x, y: forbidden_resources.append(
                                          (x, y)),
                                      None,
                                      None,
                                      changed, (element.Read(), ),
                                      inherited_aces=filteredaces)

    for child, child_uri in ok_resources:
        href = element.HRef.fromString(child_uri)
        try:
            yield responseForHref(
                request, responses, href, child,
                functools.partial(_namedPropertiesForResource, forbidden=False)
                if propertyreq else None, propertyreq)
        except ConcurrentModification:
            # This can happen because of a race-condition between the
            # time we determine which resources exist and the deletion
            # of one of these resources in another request.  In this
            # case, we ignore the now missing resource rather
            # than raise an error for the entire report.
            log.error("Missing resource during sync: %s" % (href, ))

    for child, child_uri in forbidden_resources:
        href = element.HRef.fromString(child_uri)
        try:
            yield responseForHref(
                request, responses, href, child,
                functools.partial(_namedPropertiesForResource, forbidden=True)
                if propertyreq else None, propertyreq)
        except ConcurrentModification:
            # This can happen because of a race-condition between the
            # time we determine which resources exist and the deletion
            # of one of these resources in another request.  In this
            # case, we ignore the now missing resource rather
            # than raise an error for the entire report.
            log.error("Missing resource during sync: %s" % (href, ))

    for name in removed:
        href = element.HRef.fromString(joinURL(request.uri, name))
        responses.append(
            element.StatusResponse(
                element.HRef.fromString(href),
                element.Status.fromResponseCode(responsecode.NOT_FOUND)))

    for name in notallowed:
        href = element.HRef.fromString(joinURL(request.uri, name))
        responses.append(
            element.StatusResponse(
                element.HRef.fromString(href),
                element.Status.fromResponseCode(responsecode.NOT_ALLOWED)))

    if not hasattr(request, "extendedLogItems"):
        request.extendedLogItems = {}
    request.extendedLogItems["responses"] = len(responses)

    responses.append(element.SyncToken.fromString(newtoken))

    returnValue(MultiStatusResponse(responses))