def report_DAV__principal_match(self, request, principal_match):
    """
    Generate a principal-match REPORT. (RFC 3744, section 9.3)
    """
    # Verify root element
    if not isinstance(principal_match, element.PrincipalMatch):
        raise ValueError("%s expected as root element, not %s."
                         % (element.PrincipalMatch.sname(), principal_match.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 a single DAV:prop element from the REPORT request body
    propertiesForResource = None
    propElement = None
    principalPropElement = None
    lookForPrincipals = True

    for child in principal_match.children:
        if child.qname() == (dav_namespace, "prop"):
            propertiesForResource = prop_common.propertyListForResource
            propElement = child

        elif child.qname() == (dav_namespace, "self"):
            lookForPrincipals = True

        elif child.qname() == (dav_namespace, "principal-property"):
            # Must have one and only one property in this element
            if len(child.children) != 1:
                log.error("Wrong number of properties in DAV:principal-property: %s"
                          % (len(child.children),))
                raise HTTPError(StatusResponse(
                    responsecode.BAD_REQUEST,
                    "DAV:principal-property must contain exactly one property"
                ))

            lookForPrincipals = False
            principalPropElement = child.children[0]

    # Run report for each referenced principal
    try:
        responses = []
        matchcount = 0

        myPrincipalURL = self.currentPrincipal(request).children[0]

        if lookForPrincipals:

            # Find the set of principals that represent "self".

            # First add "self"
            principal = waitForDeferred(request.locateResource(str(myPrincipalURL)))
            yield principal
            principal = principal.getResult()
            selfItems = [principal, ]

            # Get group memberships for "self" and add each of those
            d = waitForDeferred(principal.groupMemberships())
            yield d
            memberships = d.getResult()
            selfItems.extend(memberships)

            # Now add each principal found to the response provided the principal resource is a child of
            # the current resource.
            for principal in selfItems:
                # Get all the URIs that point to the principal resource
                # FIXME: making the assumption that the principalURL() is the URL of the resource we found
                principal_uris = [principal.principalURL()]
                principal_uris.extend(principal.alternateURIs())

                # Compare each one to the request URI and return at most one that matches
                for uri in principal_uris:
                    if uri.startswith(request.uri):
                        # Check size of results is within limit
                        matchcount += 1
                        if matchcount > max_number_of_matches:
                            raise NumberOfMatchesWithinLimits(max_number_of_matches)

                        d = waitForDeferred(prop_common.responseForHref(
                            request,
                            responses,
                            element.HRef.fromString(uri),
                            principal,
                            propertiesForResource,
                            propElement
                        ))
                        yield d
                        d.getResult()
                        break
        else:
            # 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 = waitForDeferred(self.inheritedACEsforChildren(request))
            yield filteredaces
            filteredaces = filteredaces.getResult()

            children = []
            d = waitForDeferred(self.findChildren("infinity", request, lambda x, y: children.append((x, y)),
                                                  privileges=(element.Read(),), inherited_aces=filteredaces))
            yield d
            d.getResult()

            for child, uri in children:
                # Try to read the requested property from this resource
                try:
                    prop = waitForDeferred(child.readProperty(principalPropElement.qname(), request))
                    yield prop
                    prop = prop.getResult()
                    if prop:
                        prop.removeWhitespaceNodes()

                    if prop and len(prop.children) == 1 and isinstance(prop.children[0], element.HRef):
                        # Find principal associated with this property and test it
                        principal = waitForDeferred(request.locateResource(str(prop.children[0])))
                        yield principal
                        principal = principal.getResult()

                        if principal and isPrincipalResource(principal):
                            d = waitForDeferred(principal.principalMatch(myPrincipalURL))
                            yield d
                            matched = d.getResult()
                            if matched:
                                # Check size of results is within limit
                                matchcount += 1
                                if matchcount > max_number_of_matches:
                                    raise NumberOfMatchesWithinLimits(max_number_of_matches)

                                d = waitForDeferred(prop_common.responseForHref(
                                    request,
                                    responses,
                                    element.HRef.fromString(uri),
                                    child,
                                    propertiesForResource,
                                    propElement
                                ))
                                yield d
                                d.getResult()
                except HTTPError:
                    # Just ignore a failure to access the property. We treat this like a property that does not exist
                    # or does not match the principal.
                    pass

    except NumberOfMatchesWithinLimits:
        log.error("Too many matching components in principal-match report")
        raise HTTPError(ErrorResponse(
            responsecode.FORBIDDEN,
            element.NumberOfMatchesWithinLimits()
        ))

    yield MultiStatusResponse(responses)
Esempio n. 2
0
def 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."
                         % (element.PrincipalPropertySearch.sname(), principal_property_search.sname()))

    # Only handle Depth: 0
    depth = request.headers.getHeader("depth", "0")
    if depth != "0":
        log.error("Error in prinicpal-property-search REPORT, Depth set to %s" % (depth,))
        raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, "Depth %s not allowed" % (depth,)))
    
    # 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)
            propertySearches.append((props.children, str(match).lower()))
    
    def nodeMatch(node, match):
        """
        See if the content of the supplied node matches the supplied text.
        Try to follow the matching guidance in rfc3744 section 9.4.1.
        @param prop:  the property element to match.
        @param match: the text to match against.
        @return:      True if the property matches, False otherwise.
        """
        node.removeWhitespaceNodes()
        for child in node.children:
            if isinstance(child, PCDATAElement):
                comp = str(child).lower()
                if comp.find(match) != -1:
                    return True
            else:
                return nodeMatch(child, match)
        else:
            return False
        
    def propertySearch(resource, request):
        """
        Test the resource to see if it contains properties matching the
        property-search specification in this report.
        @param resource: the L{DAVFile} for the resource to test.
        @param request:  the current request.
        @return:         True if the resource has matching properties, False otherwise.
        """
        for props, match in propertySearches:
            # Test each property
            for prop in props:
                try:
                    propvalue = waitForDeferred(resource.readProperty(prop.qname(), request))
                    yield propvalue
                    propvalue = propvalue.getResult()
                    if propvalue and not nodeMatch(propvalue, match):
                        yield False
                        return
                except HTTPError:
                    # No property => no match
                    yield False
                    return
        
        yield True

    propertySearch = deferredGenerator(propertySearch)

    # Run report
    try:
        resources = []
        responses = []
        matchcount = 0

        if applyTo:
            for principalCollection in self.principalCollections():
                uri = principalCollection.principalCollectionURL()
                resource = waitForDeferred(request.locateResource(uri))
                yield resource
                resource = resource.getResult()
                if resource:
                    resources.append((resource, uri))
        else:
            resources.append((self, request.uri))

        # Loop over all collections and principal resources within
        for resource, ruri in resources:

            # 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 = waitForDeferred(resource.inheritedACEsforChildren(request))
            yield filteredaces
            filteredaces = filteredaces.getResult()

            children = []
            d = waitForDeferred(resource.findChildren("infinity", request, lambda x, y: children.append((x,y)),
                                                      privileges=(element.Read(),), inherited_aces=filteredaces))
            yield d
            d.getResult()

            for child, uri in children:
                if isPrincipalResource(child):
                    d = waitForDeferred(propertySearch(child, request))
                    yield d
                    d = d.getResult()
                    if d:
                        # Check size of results is within limit
                        matchcount += 1
                        if matchcount > max_number_of_matches:
                            raise NumberOfMatchesWithinLimits(max_number_of_matches)
    
                        d = waitForDeferred(prop_common.responseForHref(
                            request,
                            responses,
                            element.HRef.fromString(uri),
                            child,
                            propertiesForResource,
                            propElement
                        ))
                        yield d
                        d.getResult()

    except NumberOfMatchesWithinLimits:
        log.error("Too many matching components in prinicpal-property-search report")
        raise HTTPError(ErrorResponse(
            responsecode.FORBIDDEN,
            element.NumberOfMatchesWithinLimits()
        ))

    yield MultiStatusResponse(responses)
Esempio n. 3
0
def report_DAV__principal_match(self, request, principal_match):
    """
    Generate a principal-match REPORT. (RFC 3744, section 9.3)
    """
    # Verify root element
    if not isinstance(principal_match, element.PrincipalMatch):
        raise ValueError(
            "%s expected as root element, not %s." %
            (element.PrincipalMatch.sname(), principal_match.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 a single DAV:prop element from the REPORT request body
    propertiesForResource = None
    propElement = None
    principalPropElement = None
    lookForPrincipals = True

    for child in principal_match.children:
        if child.qname() == (dav_namespace, "prop"):
            propertiesForResource = prop_common.propertyListForResource
            propElement = child

        elif child.qname() == (dav_namespace, "self"):
            lookForPrincipals = True

        elif child.qname() == (dav_namespace, "principal-property"):
            # Must have one and only one property in this element
            if len(child.children) != 1:
                log.error(
                    "Wrong number of properties in DAV:principal-property: %s"
                    % (len(child.children), ))
                raise HTTPError(
                    StatusResponse(
                        responsecode.BAD_REQUEST,
                        "DAV:principal-property must contain exactly one property"
                    ))

            lookForPrincipals = False
            principalPropElement = child.children[0]

    # Run report for each referenced principal
    try:
        responses = []
        matchcount = 0

        myPrincipalURL = self.currentPrincipal(request).children[0]

        if lookForPrincipals:

            # Find the set of principals that represent "self".

            # First add "self"
            principal = waitForDeferred(
                request.locateResource(str(myPrincipalURL)))
            yield principal
            principal = principal.getResult()
            selfItems = [
                principal,
            ]

            # Get group memberships for "self" and add each of those
            d = waitForDeferred(principal.groupMemberships())
            yield d
            memberships = d.getResult()
            selfItems.extend(memberships)

            # Now add each principal found to the response provided the principal resource is a child of
            # the current resource.
            for principal in selfItems:
                # Get all the URIs that point to the principal resource
                # FIXME: making the assumption that the principalURL() is the URL of the resource we found
                principal_uris = [principal.principalURL()]
                principal_uris.extend(principal.alternateURIs())

                # Compare each one to the request URI and return at most one that matches
                for uri in principal_uris:
                    if uri.startswith(request.uri):
                        # Check size of results is within limit
                        matchcount += 1
                        if matchcount > max_number_of_matches:
                            raise NumberOfMatchesWithinLimits(
                                max_number_of_matches)

                        d = waitForDeferred(
                            prop_common.responseForHref(
                                request, responses,
                                element.HRef.fromString(uri), principal,
                                propertiesForResource, propElement))
                        yield d
                        d.getResult()
                        break
        else:
            # 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 = waitForDeferred(
                self.inheritedACEsforChildren(request))
            yield filteredaces
            filteredaces = filteredaces.getResult()

            children = []
            d = waitForDeferred(
                self.findChildren("infinity",
                                  request,
                                  lambda x, y: children.append((x, y)),
                                  privileges=(element.Read(), ),
                                  inherited_aces=filteredaces))
            yield d
            d.getResult()

            for child, uri in children:
                # Try to read the requested property from this resource
                try:
                    prop = waitForDeferred(
                        child.readProperty(principalPropElement.qname(),
                                           request))
                    yield prop
                    prop = prop.getResult()
                    if prop:
                        prop.removeWhitespaceNodes()

                    if prop and len(prop.children) == 1 and isinstance(
                            prop.children[0], element.HRef):
                        # Find principal associated with this property and test it
                        principal = waitForDeferred(
                            request.locateResource(str(prop.children[0])))
                        yield principal
                        principal = principal.getResult()

                        if principal and isPrincipalResource(principal):
                            d = waitForDeferred(
                                principal.principalMatch(myPrincipalURL))
                            yield d
                            matched = d.getResult()
                            if matched:
                                # Check size of results is within limit
                                matchcount += 1
                                if matchcount > max_number_of_matches:
                                    raise NumberOfMatchesWithinLimits(
                                        max_number_of_matches)

                                d = waitForDeferred(
                                    prop_common.responseForHref(
                                        request, responses,
                                        element.HRef.fromString(uri), child,
                                        propertiesForResource, propElement))
                                yield d
                                d.getResult()
                except HTTPError:
                    # Just ignore a failure to access the property. We treat this like a property that does not exist
                    # or does not match the principal.
                    pass

    except NumberOfMatchesWithinLimits:
        log.error("Too many matching components in principal-match report")
        raise HTTPError(
            ErrorResponse(responsecode.FORBIDDEN,
                          element.NumberOfMatchesWithinLimits()))

    yield MultiStatusResponse(responses)