Пример #1
0
    def http_POST(self, request):
        """
        POST method with JSON body is used for control.
        """

        #
        # Check authentication and access controls
        #
        yield self.authorize(request, (davxml.Read(),))

        contentType = request.headers.getHeader("content-type")
        # Check content first
        if "{}/{}".format(contentType.mediaType, contentType.mediaSubtype) != "application/json":
            self.log.error("MIME type {mime} not allowed in request", mime=contentType)
            raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, "MIME type {} not allowed in request".format(contentType)))

        body = (yield allDataFromStream(request.stream))
        try:
            j = json.loads(body)
        except (ValueError, TypeError) as e:
            self.log.error("Invalid JSON data in request: {ex}\n{body}", ex=e, body=body)
            raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, "Invalid JSON data in request: {}\n{}".format(e, body)))

        try:
            action = j["action"]
        except KeyError:
            self._error("error", "No 'action' member in root JSON object.")

        method = "action_{}".format(action)
        if not hasattr(self, method):
            self._error("error", "The action '{}' is not supported.".format(action))

        result = yield getattr(self, method)(j)
        returnValue(result)
Пример #2
0
    def submitRequest(self, request, *args, **kwargs):
        """
        Select an available client and perform the given request on it.

        @param command: A C{str} representing an attribute of
            L{MemCacheProtocol}.
        @parma args: Any positional arguments that should be passed to
            C{command}.
        @param kwargs: Any keyword arguments that should be passed to
            C{command}.

        @return: A L{Deferred} that fires with the result of the given command.
        """

        # Since we may need to replay the request we have to read the request.stream
        # into memory and reset request.stream to use a MemoryStream each time we repeat
        # the request
        data = (yield allDataFromStream(request.stream))

        # Try this maxRetries times
        for ctr in xrange(self.maxRetries + 1):
            try:
                request.stream = MemoryStream(data if data is not None else "")
                request.stream.doStartReading = None

                response = (yield self._submitRequest(request, args, kwargs))

            except (ConnectionLost, ConnectionDone, ConnectError), e:
                self.log.error("HTTP pooled client connection error (attempt: %d) - retrying: %s" % (ctr + 1, e,))
                continue

            # TODO: find the proper cause of these assertions and fix
            except (AssertionError,), e:
                self.log.error("HTTP pooled client connection assertion error (attempt: %d) - retrying: %s" % (ctr + 1, e,))
                continue
Пример #3
0
    def test_receive_fake_conduit(self):
        """
        Cross-pod request works when conduit does support the action.
        """

        store = self.storeUnderTest()
        self.patch(store, "conduit", self.FakeConduit(store))

        request = SimpleRequest(
            self.site,
            "POST",
            "/conduit",
            headers=http_headers.Headers(rawHeaders={
                "Content-Type": ("application/json",),
                self.thisServer.secretHeader()[0]: self.thisServer.secretHeader()[1],
            }),
            content="""
{
    "action":"fake",
    "echo":"bravo"
}
""".replace("\n", "\r\n")
        )

        response = (yield self.send(request))
        self.assertEqual(response.code, responsecode.OK)
        data = (yield allDataFromStream(response.stream))
        j = json.loads(data)
        self.assertTrue("result" in j)
        self.assertEqual(j["result"], "ok")
        self.assertTrue("back2u" in j)
        self.assertEqual(j["back2u"], "bravo")
        self.assertTrue("more" in j)
        self.assertEqual(j["more"], "bits")
Пример #4
0
    def http_POST(self, request):
        """
        POST method with JSON body is used for control.
        """

        #
        # Check authentication and access controls
        #
        yield self.authorize(request, (davxml.Read(),))

        contentType = request.headers.getHeader("content-type")
        # Check content first
        if "{}/{}".format(contentType.mediaType, contentType.mediaSubtype) != "application/json":
            self.log.error("MIME type {mime} not allowed in request", mime=contentType)
            raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, "MIME type {} not allowed in request".format(contentType)))

        body = (yield allDataFromStream(request.stream))
        try:
            j = json.loads(body)
        except (ValueError, TypeError) as e:
            self.log.error("Invalid JSON data in request: {ex}\n{body}", ex=e, body=body)
            raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, "Invalid JSON data in request: {}\n{}".format(e, body)))

        try:
            action = j["action"]
        except KeyError:
            self._error("error", "No 'action' member in root JSON object.")

        method = "action_{}".format(action)
        if not hasattr(self, method):
            self._error("error", "The action '{}' is not supported.".format(action))

        result = yield getattr(self, method)(j)
        returnValue(result)
Пример #5
0
    def logResponse(self, response):
        """
        Log an HTTP request.
        """
        iostr = StringIO()
        iostr.write(">>>> Response start\n\n")
        code_message = responsecode.RESPONSES.get(response.code, "Unknown Status")
        iostr.write("HTTP/1.1 {:d} {}\n".format(response.code, code_message))
        for name, valuelist in response.headers.getAllRawHeaders():
            for value in valuelist:
                # Do not log authorization details
                if name not in ("WWW-Authenticate",):
                    iostr.write("{}: {}\n".format(name, value))
                else:
                    iostr.write("{}: xxxxxxxxx\n".format(name))
        iostr.write("\n")

        # We need to play a trick with the response stream to ensure we don't mess it up. So we
        # read it, store the value in a MemoryStream, and replace the response's stream with that,
        # so the data can be read again.
        data = (yield allDataFromStream(response.stream))
        iostr.write(data)
        response.stream = MemoryStream(data if data is not None else "")
        response.stream.doStartReading = None

        iostr.write("\n\n>>>> Response end\n")
        returnValue(iostr.getvalue())
Пример #6
0
    def doRequest(self, txn):

        # Generate an HTTP client request
        try:
            if "xpod" not in txn.logItems:
                txn.logItems["xpod"] = 0
            txn.logItems["xpod"] += 1

            response = (yield self._processRequest())

            if accountingEnabledForCategory("xPod"):
                self.loggedResponse = yield self.logResponse(response)
                emitAccounting("xPod", "", self.loggedRequest + "\n" + self.loggedResponse, "POST")

            if response.code in (responsecode.OK,):
                data = (yield allDataFromStream(response.stream))
                data = json.loads(data)
            else:
                raise ValueError("Incorrect cross-pod response status code: {}".format(response.code))

        except Exception as e:
            # Request failed
            log.error("Could not do cross-pod request : {request} {ex}", request=self, ex=e)
            raise ValueError("Failed cross-pod request: {}".format(e))

        returnValue(data)
Пример #7
0
    def test_receive_fake_conduit(self):
        """
        Cross-pod request works when conduit does support the action.
        """

        store = self.storeUnderTest()
        self.patch(store, "conduit", self.FakeConduit(store))

        request = SimpleRequest(self.site,
                                "POST",
                                "/conduit",
                                headers=http_headers.Headers(
                                    rawHeaders={
                                        "Content-Type": ("application/json", ),
                                        self.thisServer.secretHeader()[0]:
                                        self.thisServer.secretHeader()[1],
                                    }),
                                content="""
{
    "action":"fake",
    "echo":"bravo"
}
""".replace("\n", "\r\n"))

        response = (yield self.send(request))
        self.assertEqual(response.code, responsecode.OK)
        data = (yield allDataFromStream(response.stream))
        j = json.loads(data)
        self.assertTrue("result" in j)
        self.assertEqual(j["result"], "ok")
        self.assertTrue("value" in j)
        self.assertEqual(j["value"], {"back2u": "bravo", "more": "bits"})
Пример #8
0
    def test_receive_ping(self):
        """
        Cross-pod request works with the "ping" action.
        """

        request = SimpleRequest(self.site,
                                "POST",
                                "/conduit",
                                headers=http_headers.Headers(
                                    rawHeaders={
                                        "Content-Type": ("application/json", ),
                                        self.thisServer.secretHeader()[0]:
                                        self.thisServer.secretHeader()[1],
                                    }),
                                content="""
{
    "action":"ping"
}
""".replace("\n", "\r\n"))

        response = (yield self.send(request))
        self.assertEqual(response.code, responsecode.OK)
        data = (yield allDataFromStream(response.stream))
        j = json.loads(data)
        self.assertTrue("result" in j)
        self.assertEqual(j["result"], "ok")
Пример #9
0
        def _verify(hdrs, body, keys, result, sign_headers=("Originator", "Recipient", "Content-Type",), manipulate_request=None):
            for algorithm in ("rsa-sha1", "rsa-sha256",):
                # Create signature
                stream = MemoryStream(body)
                headers = Headers()
                for name, value in [hdr.split(":", 1) for hdr in hdrs.splitlines()]:
                    headers.addRawHeader(name, value)
                request = DKIMRequest("POST", "/", headers, stream, "example.com", "dkim", self.private_keyfile, algorithm, sign_headers, True, True, True, 3600)
                yield request.sign()

                # Possibly munge the request after the signature is done
                if manipulate_request is not None:
                    manipulate_request(request)

                # Verify signature
                TestPublicKeyLookup.PublicKeyLookup_Testing.keys = keys
                data = (yield allDataFromStream(request.stream))
                verifier = DKIMVerifier(request.headers, data, key_lookup=(TestPublicKeyLookup.PublicKeyLookup_Testing,))
                TestPublicKeyLookup.PublicKeyLookup_Testing.flushCache()
                try:
                    yield verifier.verify()
                except Exception, e:
                    if result:
                        self.fail("DKIMVerifier:verify failed: %s" % (e,))
                else:
                    if not result:
                        self.fail("DKIMVerifier:verify did not fail")
Пример #10
0
    def test_receive_ping(self):
        """
        Cross-pod request works with the "ping" action.
        """

        request = SimpleRequest(
            self.site,
            "POST",
            "/conduit",
            headers=http_headers.Headers(rawHeaders={
                "Content-Type": ("application/json",),
                self.thisServer.secretHeader()[0]: self.thisServer.secretHeader()[1],
            }),
            content="""
{
    "action":"ping"
}
""".replace("\n", "\r\n")
        )

        response = (yield self.send(request))
        self.assertEqual(response.code, responsecode.OK)
        data = (yield allDataFromStream(response.stream))
        j = json.loads(data)
        self.assertTrue("result" in j)
        self.assertEqual(j["result"], "ok")
Пример #11
0
        def _verify(hdrs, body, keys, result, sign_headers=("Originator", "Recipient", "Content-Type",), manipulate_request=None):
            for algorithm in ("rsa-sha1", "rsa-sha256",):
                # Create signature
                stream = MemoryStream(body)
                headers = Headers()
                for name, value in [hdr.split(":", 1) for hdr in hdrs.splitlines()]:
                    headers.addRawHeader(name, value)
                request = DKIMRequest("POST", "/", headers, stream, "example.com", "dkim", self.private_keyfile, algorithm, sign_headers, True, True, True, 3600)
                yield request.sign()

                # Possibly munge the request after the signature is done
                if manipulate_request is not None:
                    manipulate_request(request)

                # Verify signature
                TestPublicKeyLookup.PublicKeyLookup_Testing.keys = keys
                data = (yield allDataFromStream(request.stream))
                verifier = DKIMVerifier(request.headers, data, key_lookup=(TestPublicKeyLookup.PublicKeyLookup_Testing,))
                TestPublicKeyLookup.PublicKeyLookup_Testing({}).flushCache()
                try:
                    yield verifier.verify()
                except Exception, e:
                    if result:
                        self.fail("DKIMVerifier:verify failed: %s" % (e,))
                else:
                    if not result:
                        self.fail("DKIMVerifier:verify did not fail")
Пример #12
0
    def logRequest(self, request):
        """
        Log an HTTP request.
        """

        iostr = StringIO()
        iostr.write(">>>> Request start\n\n")
        if hasattr(request, "clientproto"):
            protocol = "HTTP/{:d}.{:d}".format(request.clientproto[0], request.clientproto[1])
        else:
            protocol = "HTTP/1.1"
        iostr.write("{} {} {}\n".format(request.method, request.uri, protocol))
        for name, valuelist in request.headers.getAllRawHeaders():
            for value in valuelist:
                # Do not log authorization details
                if name not in ("Authorization",):
                    iostr.write("{}: {}\n".format(name, value))
                else:
                    iostr.write("{}: xxxxxxxxx\n".format(name))
        iostr.write("\n")

        # We need to play a trick with the request stream as we can only read it once. So we
        # read it, store the value in a MemoryStream, and replace the request's stream with that,
        # so the data can be read again. Note if we are sending an attachment, we won't log
        # the attachment data as we do not want to read it all into memory.
        if self.stream is None:
            data = (yield allDataFromStream(request.stream))
            iostr.write(data)
            request.stream = MemoryStream(data if data is not None else "")
            request.stream.doStartReading = None
        else:
            iostr.write("<<Stream Type: {}>>\n".format(self.streamType))

        iostr.write("\n\n>>>> Request end\n")
        returnValue(iostr.getvalue())
Пример #13
0
    def doRequest(self, txn):

        # Generate an HTTP client request
        try:
            if "xpod" not in txn.logItems:
                txn.logItems["xpod"] = 0
            txn.logItems["xpod"] += 1

            response = (yield self._processRequest())

            if accountingEnabledForCategory("xPod"):
                self.loggedResponse = yield self.logResponse(response)
                emitAccounting("xPod", "", self.loggedRequest + "\n" + self.loggedResponse, "POST")

            if response.code == responsecode.OK:
                if self.writeStream is None:
                    data = (yield allDataFromStream(response.stream))
                    data = json.loads(data)
                else:
                    yield readStream(response.stream, self.writeStream.write)
                    content_type = response.headers.getHeader("content-type")
                    if content_type is None:
                        content_type = MimeType("application", "octet-stream")
                    content_disposition = response.headers.getHeader("content-disposition")
                    if content_disposition is None or "filename" not in content_disposition.params:
                        filename = ""
                    else:
                        filename = content_disposition.params["filename"]
                    self.writeStream.resetDetails(content_type, filename)
                    yield self.writeStream.loseConnection()
                    data = {
                        "result": "ok",
                        "content-type": content_type,
                        "name": filename,
                    }
            elif response.code == responsecode.BAD_REQUEST:
                data = (yield allDataFromStream(response.stream))
                data = json.loads(data)
            else:
                raise ValueError("Incorrect cross-pod response status code: {}".format(response.code))

        except Exception as e:
            # Request failed
            log.error("Could not do cross-pod request : {request} {ex}", request=self, ex=e)
            raise ValueError("Failed cross-pod request: {}".format(e))

        returnValue(data)
Пример #14
0
    def http_POST(self, request):
        """
        The server-to-server POST method.
        """

        # Need a transaction to work with
        txn = transactionFromRequest(request, self._newStore)

        # Log extended item
        if not hasattr(request, "extendedLogItems"):
            request.extendedLogItems = {}

        # This is a server-to-server scheduling operation.
        scheduler = IScheduleScheduler(txn,
                                       None,
                                       logItems=request.extendedLogItems,
                                       podding=self._podding)

        # Check content first
        contentType = request.headers.getHeader("content-type")
        format = self.determineType(contentType)

        if format is None:
            msg = "MIME type {} not allowed in iSchedule request".format(
                contentType, )
            self.log.error(msg)
            raise HTTPError(
                scheduler.errorResponse(
                    responsecode.FORBIDDEN,
                    (ischedule_namespace, "invalid-calendar-data-type"),
                    msg,
                ))

        originator = self.loadOriginatorFromRequestHeaders(request)
        recipients = self.loadRecipientsFromRequestHeaders(request)
        body = (yield allDataFromStream(request.stream))
        calendar = Component.fromString(body, format=format)

        # Do the POST processing treating this as a non-local schedule
        try:
            result = (yield
                      scheduler.doSchedulingViaPOST(request.remoteAddr,
                                                    request.headers, body,
                                                    calendar, originator,
                                                    recipients))
        except Exception:
            ex = Failure()
            yield txn.abort()
            ex.raiseException()
        else:
            yield txn.commit()
        response = result.response(format=format)
        if not self._podding:
            response.headers.addRawHeader(
                ISCHEDULE_CAPABILITIES,
                str(config.Scheduling.iSchedule.SerialNumber))
        returnValue(response)
Пример #15
0
    def assertResponse(self, response, expected):
        self.assertNotEquals(response, None, "Got None instead of a response.")
        self.assertEquals(response.code, expected[0])
        self.assertEquals(set(response.headers.getAllRawHeaders()),
                          set(expected[1].getAllRawHeaders()))

        d = allDataFromStream(response.stream)
        d.addCallback(self.assertEquals, expected[2])
        return d
Пример #16
0
    def assertResponse(self, response, expected):
        self.assertNotEquals(response, None, "Got None instead of a response.")
        self.assertEquals(response.code, expected[0])
        self.assertEquals(set(response.headers.getAllRawHeaders()),
                          set(expected[1].getAllRawHeaders()))

        d = allDataFromStream(response.stream)
        d.addCallback(self.assertEquals, expected[2])
        return d
Пример #17
0
    def http_POST(self, request):
        """
        The server-to-server POST method.
        """

        # Check shared secret
        if not self.store.directoryService().serversDB.getThisServer().checkSharedSecret(request.headers):
            self.log.error("Invalid shared secret header in cross-pod request")
            raise HTTPError(StatusResponse(responsecode.FORBIDDEN, "Not authorized to make this request"))

        # Look for XPOD header
        xpod = request.headers.getRawHeaders("XPOD")
        contentType = request.headers.getHeader("content-type")
        if xpod is not None:
            # Attachments are sent in the request body with the JSON data in a header. We
            # decode the header and add the request.stream as an attribute of the JSON object.
            xpod = xpod[0]
            try:
                j = json.loads(base64.b64decode(xpod))
            except (TypeError, ValueError) as e:
                self.log.error("Invalid JSON header in request: {ex}\n{xpod}", ex=e, xpod=xpod)
                raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, "Invalid JSON header in request: {}\n{}".format(e, xpod)))
            j["stream"] = request.stream
            j["streamType"] = contentType
        else:
            # Check content first
            if "{}/{}".format(contentType.mediaType, contentType.mediaSubtype) != "application/json":
                self.log.error("MIME type {mime} not allowed in request", mime=contentType)
                raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, "MIME type {} not allowed in request".format(contentType)))

            body = (yield allDataFromStream(request.stream))
            try:
                j = json.loads(body)
            except ValueError as e:
                self.log.error("Invalid JSON data in request: {ex}\n{body}", ex=e, body=body)
                raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, "Invalid JSON data in request: {}\n{}".format(e, body)))

        # Log extended item
        if not hasattr(request, "extendedLogItems"):
            request.extendedLogItems = {}
        request.extendedLogItems["xpod"] = j["action"] if "action" in j else "unknown"

        # Get the conduit to process the data
        try:
            result = yield self.store.conduit.processRequest(j)
        except FailedCrossPodRequestError as e:
            raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, str(e)))
        except Exception as e:
            raise HTTPError(StatusResponse(responsecode.INTERNAL_SERVER_ERROR, str(e)))

        response = JSONResponse(responsecode.OK, result)
        returnValue(response)
Пример #18
0
    def bodyHash(self):
        """
        Generate the hash of the request body data.
        """

        # We need to play a trick with the request stream as we can only read it once. So we
        # read it, store the value in a MemoryStream, and replace the request's stream with that,
        # so the data can be read again.
        data = (yield allDataFromStream(self.stream))
        self.stream = MemoryStream(data if data is not None else "")
        self.stream.doStartReading = None

        returnValue(base64.b64encode(self.hash_method(DKIMUtils.canonicalizeBody(data)).digest()))
Пример #19
0
    def xmlRequestHandler(self, request):

        # Need to read the data and get the root element first
        xmldata = (yield allDataFromStream(request.stream))
        try:
            doc = element.WebDAVDocument.fromString(xmldata)
        except ValueError, e:
            self.log.error("Error parsing doc (%s) Doc:\n %s" % (str(e), xmldata,))
            raise HTTPError(ErrorResponse(
                responsecode.FORBIDDEN,
                (customxml.calendarserver_namespace, "valid-request"),
                "Invalid XML",
            ))
Пример #20
0
    def xmlRequestHandler(self, request):

        # Need to read the data and get the root element first
        xmldata = (yield allDataFromStream(request.stream))
        try:
            doc = element.WebDAVDocument.fromString(xmldata)
        except ValueError, e:
            self.log.error("Error parsing doc ({ex}) Doc:\n {x}", ex=str(e), x=xmldata)
            raise HTTPError(ErrorResponse(
                responsecode.FORBIDDEN,
                (customxml.calendarserver_namespace, "valid-request"),
                "Invalid XML",
            ))
Пример #21
0
    def bodyHash(self):
        """
        Generate the hash of the request body data.
        """

        # We need to play a trick with the request stream as we can only read it once. So we
        # read it, store the value in a MemoryStream, and replace the request's stream with that,
        # so the data can be read again.
        data = (yield allDataFromStream(self.stream))
        self.stream = MemoryStream(data if data is not None else "")
        self.stream.doStartReading = None

        returnValue(base64.b64encode(self.hash_method(DKIMUtils.canonicalizeBody(data)).digest()))
Пример #22
0
        def listChildrenViaPropfind():
            authPrincipal = yield self.actualRoot.findPrincipalForAuthID("user01")
            request = SimpleStoreRequest(self, "PROPFIND", "/calendars/__uids__/user01/", authPrincipal=authPrincipal)
            request.headers.setHeader("depth", "1")
            response = yield self.send(request)
            response = IResponse(response)
            data = yield allDataFromStream(response.stream)

            tree = XML(data)
            seq = [e.text for e in tree.findall("{DAV:}response/{DAV:}href")]
            shortest = min(seq, key=len)
            seq.remove(shortest)
            filtered = [elem[len(shortest):].rstrip("/") for elem in seq]
            returnValue(filtered)
Пример #23
0
    def _doPOSTSharerAccept(self, body, resultcode=responsecode.OK, sharer="user02"):
        authPrincipal = yield self.actualRoot.findPrincipalForAuthID(sharer)
        request = SimpleStoreRequest(self, "POST", "/calendars/__uids__/{}/".format(sharer), content=body, authPrincipal=authPrincipal)
        request.headers.setHeader("content-type", MimeType("text", "xml"))
        response = yield self.send(request)
        response = IResponse(response)
        self.assertEqual(response.code, resultcode)

        if response.stream:
            xmldata = yield allDataFromStream(response.stream)
            doc = WebDAVDocument.fromString(xmldata)
            returnValue(doc)
        else:
            returnValue(None)
Пример #24
0
    def _doPOST(self, body, resultcode=responsecode.OK):
        authPrincipal = yield self.actualRoot.findPrincipalForAuthID("user01")
        request = SimpleStoreRequest(self, "POST", "/calendars/__uids__/user01/calendar/", content=body, authPrincipal=authPrincipal)
        request.headers.setHeader("content-type", MimeType("text", "xml"))
        response = yield self.send(request)
        response = IResponse(response)
        self.assertEqual(response.code, resultcode)

        # Reload resource
        self.resource = yield self._getResource()

        if response.stream:
            data = yield allDataFromStream(response.stream)
            returnValue(data)
        else:
            returnValue(None)
Пример #25
0
    def submitRequest(self, request, *args, **kwargs):
        """
        Select an available client and perform the given request on it.

        @param command: A C{str} representing an attribute of
            L{MemCacheProtocol}.
        @parma args: Any positional arguments that should be passed to
            C{command}.
        @param kwargs: Any keyword arguments that should be passed to
            C{command}.

        @return: A L{Deferred} that fires with the result of the given command.
        """

        # Since we may need to replay the request we have to read the request.stream
        # into memory and reset request.stream to use a MemoryStream each time we repeat
        # the request
        data = (yield allDataFromStream(request.stream))

        # Try this maxRetries times
        for ctr in xrange(self.maxRetries + 1):
            try:
                request.stream = MemoryStream(data if data is not None else "")
                request.stream.doStartReading = None

                response = (yield self._submitRequest(request, args, kwargs))

            except (ConnectionLost, ConnectionDone, ConnectError), e:
                self.log.error(
                    "HTTP pooled client connection error (attempt: %d) - retrying: %s"
                    % (
                        ctr + 1,
                        e,
                    ))
                continue

            # TODO: find the proper cause of these assertions and fix
            except (AssertionError, ), e:
                self.log.error(
                    "HTTP pooled client connection assertion error (attempt: %d) - retrying: %s"
                    % (
                        ctr + 1,
                        e,
                    ))
                continue
Пример #26
0
        def listChildrenViaPropfind():
            authPrincipal = yield self.actualRoot.findPrincipalForAuthID(
                "user01")
            request = SimpleStoreRequest(self,
                                         "PROPFIND",
                                         "/calendars/__uids__/user01/",
                                         authPrincipal=authPrincipal)
            request.headers.setHeader("depth", "1")
            response = yield self.send(request)
            response = IResponse(response)
            data = yield allDataFromStream(response.stream)

            tree = XML(data)
            seq = [e.text for e in tree.findall("{DAV:}response/{DAV:}href")]
            shortest = min(seq, key=len)
            seq.remove(shortest)
            filtered = [elem[len(shortest):].rstrip("/") for elem in seq]
            returnValue(filtered)
Пример #27
0
    def http_POST(self, request):
        """
        The server-to-server POST method.
        """

        # Need a transaction to work with
        txn = transactionFromRequest(request, self._newStore)

        # This is a server-to-server scheduling operation.
        scheduler = IScheduleScheduler(txn, None, podding=self._podding)

        # Check content first
        contentType = request.headers.getHeader("content-type")
        format = self.determineType(contentType)

        if format is None:
            msg = "MIME type {} not allowed in iSchedule request".format(contentType,)
            self.log.error(msg)
            raise HTTPError(scheduler.errorResponse(
                responsecode.FORBIDDEN,
                (ischedule_namespace, "invalid-calendar-data-type"),
                msg,
            ))

        originator = self.loadOriginatorFromRequestHeaders(request)
        recipients = self.loadRecipientsFromRequestHeaders(request)
        body = (yield allDataFromStream(request.stream))
        calendar = Component.fromString(body, format=format)

        # Do the POST processing treating this as a non-local schedule
        try:
            result = (yield scheduler.doSchedulingViaPOST(request.remoteAddr, request.headers, body, calendar, originator, recipients))
        except Exception:
            ex = Failure()
            yield txn.abort()
            ex.raiseException()
        else:
            yield txn.commit()
        response = result.response(format=format)
        if not self._podding:
            response.headers.addRawHeader(ISCHEDULE_CAPABILITIES, str(config.Scheduling.iSchedule.SerialNumber))
        returnValue(response)
Пример #28
0
    def _doPOST(self, body, resultcode=responsecode.OK):
        authPrincipal = yield self.actualRoot.findPrincipalForAuthID("user01")
        request = SimpleStoreRequest(self,
                                     "POST",
                                     "/calendars/__uids__/user01/calendar/",
                                     content=body,
                                     authPrincipal=authPrincipal)
        request.headers.setHeader("content-type", MimeType("text", "xml"))
        response = yield self.send(request)
        response = IResponse(response)
        self.assertEqual(response.code, resultcode)

        # Reload resource
        self.resource = yield self._getResource()

        if response.stream:
            data = yield allDataFromStream(response.stream)
            returnValue(data)
        else:
            returnValue(None)
Пример #29
0
    def calendar_query(self, query, got_xml, expected_code=responsecode.MULTI_STATUS):

        principal = yield self.actualRoot.findPrincipalForAuthID("wsanchez")
        request = SimpleStoreRequest(self, "REPORT", "/calendars/users/wsanchez/calendar/", authPrincipal=principal)
        request.stream = MemoryStream(query.toxml())
        response = yield self.send(request)

        response = IResponse(response)

        if response.code != expected_code:
            self.fail("REPORT failed: %s" % (response.code,))

        if got_xml is not None:
            returnValue(
                (yield davXMLFromStream(response.stream).addCallback(got_xml))
            )
        else:
            returnValue(
                (yield allDataFromStream(response.stream))
            )
Пример #30
0
    def _doPOSTSharerAccept(self,
                            body,
                            resultcode=responsecode.OK,
                            sharer="user02"):
        authPrincipal = yield self.actualRoot.findPrincipalForAuthID(sharer)
        request = SimpleStoreRequest(self,
                                     "POST",
                                     "/calendars/__uids__/{}/".format(sharer),
                                     content=body,
                                     authPrincipal=authPrincipal)
        request.headers.setHeader("content-type", MimeType("text", "xml"))
        response = yield self.send(request)
        response = IResponse(response)
        self.assertEqual(response.code, resultcode)

        if response.stream:
            xmldata = yield allDataFromStream(response.stream)
            doc = WebDAVDocument.fromString(xmldata)
            returnValue(doc)
        else:
            returnValue(None)
Пример #31
0
    def _requestKey(self, request):
        """
        Get a key for this request. This depends on the method, Depth: header, authn user principal,
        request uri and a hash of the request body (the body being normalized for property order).
        """
        requestBody = (yield allDataFromStream(request.stream))
        if requestBody is not None:
            # Give it back to the request so it can be read again
            request.stream = MemoryStream(requestBody)
            request.stream.doStartReading = None

            # Normalize the property order by doing a "dumb" sort on lines
            requestLines = requestBody.splitlines()
            requestLines.sort()
            requestBody = "\n".join(requestLines)

        request.cacheKey = (request.method,
                            self._principalURI(request.authnUser), request.uri,
                            request.headers.getHeader('depth'),
                            hash(requestBody))

        returnValue(request.cacheKey)
Пример #32
0
    def _requestKey(self, request):
        """
        Get a key for this request. This depends on the method, Depth: header, authn user principal,
        request uri and a hash of the request body (the body being normalized for property order).
        """
        requestBody = (yield allDataFromStream(request.stream))
        if requestBody is not None:
            # Give it back to the request so it can be read again
            request.stream = MemoryStream(requestBody)
            request.stream.doStartReading = None

            # Normalize the property order by doing a "dumb" sort on lines
            requestLines = requestBody.splitlines()
            requestLines.sort()
            requestBody = "\n".join(requestLines)

        request.cacheKey = (request.method,
                            self._principalURI(request.authnUser),
                            request.uri,
                            request.headers.getHeader('depth'),
                            hash(requestBody))

        returnValue(request.cacheKey)
Пример #33
0
    def calendar_query(self,
                       query,
                       got_xml,
                       expected_code=responsecode.MULTI_STATUS):

        principal = yield self.actualRoot.findPrincipalForAuthID("wsanchez")
        request = SimpleStoreRequest(self,
                                     "REPORT",
                                     "/calendars/users/wsanchez/calendar/",
                                     authPrincipal=principal)
        request.stream = MemoryStream(query.toxml())
        response = yield self.send(request)

        response = IResponse(response)

        if response.code != expected_code:
            self.fail("REPORT failed: %s" % (response.code, ))

        if got_xml is not None:
            returnValue(
                (yield davXMLFromStream(response.stream).addCallback(got_xml)))
        else:
            returnValue((yield allDataFromStream(response.stream)))
Пример #34
0
    def logRequest(self, request):
        """
        Log an HTTP request.
        """

        iostr = StringIO()
        iostr.write(">>>> Request start\n\n")
        if hasattr(request, "clientproto"):
            protocol = "HTTP/{:d}.{:d}".format(request.clientproto[0],
                                               request.clientproto[1])
        else:
            protocol = "HTTP/1.1"
        iostr.write("{} {} {}\n".format(request.method, request.uri, protocol))
        for name, valuelist in request.headers.getAllRawHeaders():
            for value in valuelist:
                # Do not log authorization details
                if name not in ("Authorization", ):
                    iostr.write("{}: {}\n".format(name, value))
                else:
                    iostr.write("{}: xxxxxxxxx\n".format(name))
        iostr.write("\n")

        # We need to play a trick with the request stream as we can only read it once. So we
        # read it, store the value in a MemoryStream, and replace the request's stream with that,
        # so the data can be read again. Note if we are sending an attachment, we won't log
        # the attachment data as we do not want to read it all into memory.
        if self.stream is None:
            data = (yield allDataFromStream(request.stream))
            iostr.write(data)
            request.stream = MemoryStream(data if data is not None else "")
            request.stream.doStartReading = None
        else:
            iostr.write("<<Stream Type: {}>>\n".format(self.streamType))

        iostr.write("\n\n>>>> Request end\n")
        returnValue(iostr.getvalue())
Пример #35
0
 def checkResult(response):
     self.assertEqual(response.code, resultcode)
     if response.stream is None:
         return None
     return allDataFromStream(response.stream)
Пример #36
0
    def http_POST(self, request):
        """
        The server-to-server POST method.
        """

        # Check shared secret
        if not self.store.directoryService().serversDB().getThisServer().checkSharedSecret(request.headers):
            self.log.error("Invalid shared secret header in cross-pod request")
            raise HTTPError(StatusResponse(responsecode.FORBIDDEN, "Not authorized to make this request"))

        # Look for XPOD header
        xpod = request.headers.getRawHeaders("XPOD")
        contentType = request.headers.getHeader("content-type")
        if xpod is not None:
            # Attachments are sent in the request body with the JSON data in a header. We
            # decode the header and add the request.stream as an attribute of the JSON object.
            xpod = xpod[0]
            try:
                j = json.loads(base64.b64decode(xpod))
            except (TypeError, ValueError) as e:
                self.log.error("Invalid JSON header in request: {ex}\n{xpod}", ex=e, xpod=xpod)
                raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, "Invalid JSON header in request: {}\n{}".format(e, xpod)))
            j["stream"] = request.stream
            j["streamType"] = contentType
        else:
            # Check content first
            if "{}/{}".format(contentType.mediaType, contentType.mediaSubtype) != "application/json":
                self.log.error("MIME type {mime} not allowed in request", mime=contentType)
                raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, "MIME type {} not allowed in request".format(contentType)))

            body = (yield allDataFromStream(request.stream))
            try:
                j = json.loads(body)
            except ValueError as e:
                self.log.error("Invalid JSON data in request: {ex}\n{body}", ex=e, body=body)
                raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, "Invalid JSON data in request: {}\n{}".format(e, body)))

        # Log extended item
        if not hasattr(request, "extendedLogItems"):
            request.extendedLogItems = {}
        request.extendedLogItems["xpod"] = j["action"] if "action" in j else "unknown"

        # Look for a streaming action which needs special handling
        if self.store.conduit.isStreamAction(j):
            # Get the conduit to process the data stream
            try:

                stream = ProducerStream()
                class StreamProtocol(Protocol):
                    def connectionMade(self):
                        stream.registerProducer(self.transport, False)
                    def dataReceived(self, data):
                        stream.write(data)
                    def connectionLost(self, reason):
                        stream.finish()

                result = yield self.store.conduit.processRequestStream(j, StreamProtocol())

                try:
                    ct, name = result
                except ValueError:
                    code = responsecode.BAD_REQUEST
                else:
                    headers = {"content-type": MimeType.fromString(ct)}
                    headers["content-disposition"] = MimeDisposition("attachment", params={"filename": name})
                    returnValue(Response(responsecode.OK, headers, stream))

            except Exception as e:
                # Send the exception over to the other side
                result = {
                    "result": "exception",
                    "class": ".".join((e.__class__.__module__, e.__class__.__name__,)),
                    "details": str(e),
                }
                code = responsecode.BAD_REQUEST

        else:
            # Get the conduit to process the data
            try:
                result = yield self.store.conduit.processRequest(j)
                code = responsecode.OK if result["result"] == "ok" else responsecode.BAD_REQUEST
            except Exception as e:
                # Send the exception over to the other side
                result = {
                    "result": "exception",
                    "class": ".".join((e.__class__.__module__, e.__class__.__name__,)),
                    "details": str(e),
                }
                code = responsecode.BAD_REQUEST

        response = JSONResponse(code, result)
        returnValue(response)
Пример #37
0
    def http_POST(self, request):
        """
        The server-to-server POST method.
        """

        # Check shared secret
        if not self.store.directoryService().serversDB().getThisServer(
        ).checkSharedSecret(request.headers):
            self.log.error("Invalid shared secret header in cross-pod request")
            raise HTTPError(
                StatusResponse(responsecode.FORBIDDEN,
                               "Not authorized to make this request"))

        # Look for XPOD header
        xpod = request.headers.getRawHeaders("XPOD")
        contentType = request.headers.getHeader("content-type")
        if xpod is not None:
            # Attachments are sent in the request body with the JSON data in a header. We
            # decode the header and add the request.stream as an attribute of the JSON object.
            xpod = xpod[0]
            try:
                j = json.loads(base64.b64decode(xpod))
            except (TypeError, ValueError) as e:
                self.log.error("Invalid JSON header in request: {ex}\n{xpod}",
                               ex=e,
                               xpod=xpod)
                raise HTTPError(
                    StatusResponse(
                        responsecode.BAD_REQUEST,
                        "Invalid JSON header in request: {}\n{}".format(
                            e, xpod)))
            j["stream"] = request.stream
            j["streamType"] = contentType
        else:
            # Check content first
            if "{}/{}".format(contentType.mediaType,
                              contentType.mediaSubtype) != "application/json":
                self.log.error("MIME type {mime} not allowed in request",
                               mime=contentType)
                raise HTTPError(
                    StatusResponse(
                        responsecode.BAD_REQUEST,
                        "MIME type {} not allowed in request".format(
                            contentType)))

            body = (yield allDataFromStream(request.stream))
            try:
                j = json.loads(body)
            except ValueError as e:
                self.log.error("Invalid JSON data in request: {ex}\n{body}",
                               ex=e,
                               body=body)
                raise HTTPError(
                    StatusResponse(
                        responsecode.BAD_REQUEST,
                        "Invalid JSON data in request: {}\n{}".format(e,
                                                                      body)))

        # Log extended item
        if not hasattr(request, "extendedLogItems"):
            request.extendedLogItems = {}
        request.extendedLogItems[
            "xpod"] = j["action"] if "action" in j else "unknown"

        # Look for a streaming action which needs special handling
        if self.store.conduit.isStreamAction(j):
            # Get the conduit to process the data stream
            try:

                stream = ProducerStream()

                class StreamProtocol(Protocol):
                    def connectionMade(self):
                        stream.registerProducer(self.transport, False)

                    def dataReceived(self, data):
                        stream.write(data)

                    def connectionLost(self, reason):
                        stream.finish()

                result = yield self.store.conduit.processRequestStream(
                    j, StreamProtocol())

                try:
                    ct, name = result
                except ValueError:
                    code = responsecode.BAD_REQUEST
                else:
                    headers = {"content-type": MimeType.fromString(ct)}
                    headers["content-disposition"] = MimeDisposition(
                        "attachment", params={"filename": name})
                    returnValue(Response(responsecode.OK, headers, stream))

            except Exception as e:
                # Send the exception over to the other side
                result = {
                    "result":
                    "exception",
                    "class":
                    ".".join((
                        e.__class__.__module__,
                        e.__class__.__name__,
                    )),
                    "details":
                    str(e),
                }
                code = responsecode.BAD_REQUEST

        else:
            # Get the conduit to process the data
            try:
                result = yield self.store.conduit.processRequest(j)
                code = responsecode.OK if result[
                    "result"] == "ok" else responsecode.BAD_REQUEST
            except Exception as e:
                # Send the exception over to the other side
                result = {
                    "result":
                    "exception",
                    "class":
                    ".".join((
                        e.__class__.__module__,
                        e.__class__.__name__,
                    )),
                    "details":
                    str(e),
                }
                code = responsecode.BAD_REQUEST

        response = JSONResponse(code, result)
        returnValue(response)
Пример #38
0
 def _getResponseBody(self, key, response):
     d1 = allDataFromStream(response.stream)
     d1.addCallback(lambda responseBody: (key, responseBody))
     return d1
Пример #39
0
 def checkResult(response):
     self.assertEqual(response.code, resultcode)
     if response.stream is None:
         return None
     return allDataFromStream(response.stream)
Пример #40
0
    def http_POST(self, request):
        """
        The server-to-server POST method.
        """

        # Check shared secret
        if not self.store.directoryService().serversDB.getThisServer(
        ).checkSharedSecret(request.headers):
            self.log.error("Invalid shared secret header in cross-pod request")
            raise HTTPError(
                StatusResponse(responsecode.FORBIDDEN,
                               "Not authorized to make this request"))

        # Look for XPOD header
        xpod = request.headers.getRawHeaders("XPOD")
        contentType = request.headers.getHeader("content-type")
        if xpod is not None:
            # Attachments are sent in the request body with the JSON data in a header. We
            # decode the header and add the request.stream as an attribute of the JSON object.
            xpod = xpod[0]
            try:
                j = json.loads(base64.b64decode(xpod))
            except (TypeError, ValueError) as e:
                self.log.error("Invalid JSON header in request: {ex}\n{xpod}",
                               ex=e,
                               xpod=xpod)
                raise HTTPError(
                    StatusResponse(
                        responsecode.BAD_REQUEST,
                        "Invalid JSON header in request: {}\n{}".format(
                            e, xpod)))
            j["stream"] = request.stream
            j["streamType"] = contentType
        else:
            # Check content first
            if "{}/{}".format(contentType.mediaType,
                              contentType.mediaSubtype) != "application/json":
                self.log.error("MIME type {mime} not allowed in request",
                               mime=contentType)
                raise HTTPError(
                    StatusResponse(
                        responsecode.BAD_REQUEST,
                        "MIME type {} not allowed in request".format(
                            contentType)))

            body = (yield allDataFromStream(request.stream))
            try:
                j = json.loads(body)
            except ValueError as e:
                self.log.error("Invalid JSON data in request: {ex}\n{body}",
                               ex=e,
                               body=body)
                raise HTTPError(
                    StatusResponse(
                        responsecode.BAD_REQUEST,
                        "Invalid JSON data in request: {}\n{}".format(e,
                                                                      body)))

        # Log extended item
        if not hasattr(request, "extendedLogItems"):
            request.extendedLogItems = {}
        request.extendedLogItems[
            "xpod"] = j["action"] if "action" in j else "unknown"

        # Get the conduit to process the data
        try:
            result = yield self.store.conduit.processRequest(j)
        except FailedCrossPodRequestError as e:
            raise HTTPError(StatusResponse(responsecode.BAD_REQUEST, str(e)))
        except Exception as e:
            raise HTTPError(
                StatusResponse(responsecode.INTERNAL_SERVER_ERROR, str(e)))

        response = JSONResponse(responsecode.OK, result)
        returnValue(response)
Пример #41
0
 def _getResponseBody(self, key, response):
     d1 = allDataFromStream(response.stream)
     d1.addCallback(lambda responseBody: (key, responseBody))
     return d1