Beispiel #1
0
 def render(self, request: IRequest) -> bytes:
     # File expires a long time from now.
     # RFC-2616 section 14.21: "HTTP/1.1 servers SHOULD NOT send Expires
     # dates more than one year in the future."
     request.setHeader('expires',
                       datetimeToString(getTime() + 365 * secondsPerDay))
     return super().render(request)
Beispiel #2
0
 async def jqueryMapResource(self, request: IRequest) -> KleinRenderable:
     """
     Endpoint for the jQuery map file.
     """
     request.setHeader(HeaderName.contentType.value, ContentType.json.value)
     return await self.cachedResource(request, self.jqueryMapSourceURL,
                                      f"{self.jqueryVersion}.min.map")
    def eventSourceResource(self, request: IRequest) -> KleinRenderable:
        """
        HTML5 EventSource endpoint.
        """
        self._log.debug("Event source connected: {id}", id=id(request))

        request.setHeader(
            HeaderName.contentType.value, ContentType.eventStream.value
        )

        self.storeObserver.addListener(request)

        def disconnected(f: Failure) -> None:
            f.trap(ConnectionDone)
            self._log.debug("Event source disconnected: {id}", id=id(request))
            self.storeObserver.removeListener(request)

        def finished(_: Any) -> None:
            # We don't expect anything to fire the returned deferred, so
            # this should never happen.
            self.storeObserver.removeListener(request)
            raise AssertionError("This was not expected")

        # Handle disconnect
        request.notifyFinish().addCallbacks(finished, disconnected)

        # Return an unfired deferred, so the connection doesn't close on this
        # end...
        return Deferred()
Beispiel #4
0
    def eventSourceResource(self, request: IRequest) -> KleinRenderable:
        """
        HTML5 EventSource endpoint.
        """
        self._log.debug("Event source connected: {id}", id=id(request))

        request.setHeader(
            HeaderName.contentType.value, ContentType.eventStream.value
        )

        self.storeObserver.addListener(request)

        def disconnected(f: Failure) -> None:
            f.trap(ConnectionDone)
            self._log.debug("Event source disconnected: {id}", id=id(request))
            self.storeObserver.removeListener(request)

        def finished(_: Any) -> None:
            # We don't expect anything to fire the returned deferred, so
            # this should never happen.
            self.storeObserver.removeListener(request)
            raise AssertionError("This was not expected")

        # Handle disconnect
        request.notifyFinish().addCallbacks(finished, disconnected)

        # Return an unfired deferred, so the connection doesn't close on this
        # end...
        return Deferred()
Beispiel #5
0
 async def lscacheJSResource(self, request: IRequest) -> KleinRenderable:
     """
     Endpoint for lscache.
     """
     request.setHeader(HeaderName.contentType.value,
                       ContentType.javascript.value)
     return await self.cachedResource(request, self.lscacheJSSourceURL,
                                      f"{self.lscacheVersion}.min.js")
Beispiel #6
0
def textResponse(request: IRequest, message: str) -> KleinRenderable:
    """
    Respond with the given text.
    """
    request.setHeader(HeaderName.contentType.value, ContentType.text.value)
    request.setHeader(HeaderName.etag.value,
                      str(hash(message)).encode("ascii"))
    return message.encode("utf-8")
Beispiel #7
0
def noContentResponse(request: IRequest,
                      etag: Optional[str] = None) -> KleinRenderable:
    """
    Respond with no content.
    """
    request.setResponseCode(http.NO_CONTENT)
    if etag is not None:
        request.setHeader(HeaderName.etag.value, etag)
    return b""
def textResponse(request: IRequest, message: str) -> KleinRenderable:
    """
    Respond with the given text.
    """
    request.setHeader(HeaderName.contentType.value, ContentType.text.value)
    request.setHeader(
        HeaderName.etag.value, str(hash(message)).encode("ascii")
    )
    return message.encode("utf-8")
Beispiel #9
0
 def render(self, request: IRequest) -> bytes:
     # Pick an encoding based on client and server preferences.
     # We strongly prefer gzip because we save on bandwidth and
     # have pre-compressed the resource.
     accept = AcceptedEncodings.parse(request.getHeader('accept-encoding'))
     if 4.0 * accept['gzip'] > accept['identity']:
         request.setHeader('Content-Encoding', 'gzip')
         return self.__gzippedResource.render(request)
     else:
         return super().render(request)
def noContentResponse(
    request: IRequest, etag: Optional[str] = None
) -> KleinRenderable:
    """
    Respond with no content.
    """
    request.setResponseCode(http.NO_CONTENT)
    if etag is not None:
        request.setHeader(HeaderName.etag.value, etag)
    return b""
Beispiel #11
0
def jsonBytes(request: IRequest,
              data: bytes,
              etag: Optional[str] = None) -> bytes:
    """
    Respond with encoded JSON text.
    """
    request.setHeader(HeaderName.contentType.value, ContentType.json.value)
    if etag is None:
        etag = sha1(data).hexdigest()
    request.setHeader(HeaderName.etag.value, etag)
    return data
Beispiel #12
0
def writeJSONStream(
    request: IRequest,
    jsonStream: Iterable[bytes],
    etag: Optional[str] = None,
) -> None:
    """
    Respond with a stream of JSON data.
    """
    request.setHeader(HeaderName.contentType.value, ContentType.json.value)
    if etag is not None:
        request.setHeader(HeaderName.etag.value, etag)
    for line in jsonStream:
        request.write(line)
Beispiel #13
0
            def wrapper(app: Any, request: IRequest, *args: Any,
                        **kwargs: Any) -> KleinRenderable:
                request.setHeader(
                    HeaderName.server.value,
                    f"Incident Management System/{version}",
                )

                # Capture authentication info if sent by the client, (ie. it's
                # been previously asked to authenticate), so we can log it, but
                # don't require authentication.
                app.config.authProvider.authenticateRequest(request,
                                                            optional=True)

                return f(app, request, *args, **kwargs)
Beispiel #14
0
    def urlsEndpoint(self, request: IRequest) -> KleinRenderable:
        """
        JavaScript variables for service URLs.
        """
        urls = {
            k: getattr(URLs, k).asText()
            for k in URLs.__dict__ if not k.startswith("_")
        }

        request.setHeader(HeaderName.contentType.value,
                          ContentType.javascript.value)

        return "\n".join(("var url_{} = {};".format(k, jsonTextFromObject(v))
                          for k, v in urls.items()))
Beispiel #15
0
    async def bootstrapResource(self, request: IRequest) -> KleinRenderable:
        """
        Endpoint for Bootstrap.
        """
        requestURL = URL.fromText(request.uri.decode("ascii"))

        # Remove URL prefix
        names = requestURL.path[len(URLs.bootstrapBase.path) - 1:]

        request.setHeader(HeaderName.contentType.value, ContentType.css.value)
        return await self.cachedZippedResource(request,
                                               self.bootstrapSourceURL,
                                               self.bootstrapVersion,
                                               self.bootstrapVersion, *names)
            def wrapper(
                app: Any, request: IRequest, *args: Any, **kwargs: Any
            ) -> KleinRenderable:
                request.setHeader(
                    HeaderName.server.value,
                    f"Incident Management System/{version}",
                )

                # Capture authentication info if sent by the client, (ie. it's
                # been previously asked to authenticate), so we can log it, but
                # don't require authentication.
                app.config.authProvider.authenticateRequest(
                    request, optional=True
                )

                return f(app, request, *args, **kwargs)
Beispiel #17
0
 def mymethod(instance: Any, request: IRequest, *args: Any,
              **kw: Any) -> Any:
     data = yield _call(instance, method, request, *args, **kw)
     if _should_return_json(request):
         json_data = self._defaults.copy()
         json_data.update(data)
         for ignored in self._presentationSlots:
             json_data.pop(ignored, None)
         request.setHeader(b"content-type", b"application/json")
         ready = yield resolveDeferredObjects(json_data)
         result = dumps(ready)
     else:
         data[self.CONTENT] = loader.load()
         request.setHeader(b"content-type",
                           b"text/html; charset=utf-8")
         result = self._elementify(instance, data)
     returnValue(result)
    def urlsEndpoint(self, request: IRequest) -> KleinRenderable:
        """
        JavaScript variables for service URLs.
        """
        urls = {
            k: getattr(URLs, k).asText() for k in URLs.__dict__
            if not k.startswith("_")
        }

        request.setHeader(
            HeaderName.contentType.value, ContentType.javascript.value
        )

        return "\n".join((
            "var url_{} = {};".format(k, jsonTextFromObject(v))
            for k, v in urls.items()
        ))
Beispiel #19
0
def redirectTo(URL: bytes, request: IRequest) -> bytes:
    """
    Generate a redirect to the given location.

    @param URL: A L{bytes} giving the location to which to redirect.

    @param request: The request object to use to generate the redirect.
    @type request: L{IRequest<twisted.web.iweb.IRequest>} provider

    @raise TypeError: If the type of C{URL} a L{str} instead of L{bytes}.

    @return: A L{bytes} containing HTML which tries to convince the client
        agent
        to visit the new location even if it doesn't respect the I{FOUND}
        response code.  This is intended to be returned from a render method,
        eg::

            def render_GET(self, request):
                return redirectTo(b"http://example.com/", request)
    """
    if not isinstance(URL, bytes):
        raise TypeError("URL must be bytes")
    request.setHeader(b"Content-Type", b"text/html; charset=utf-8")
    request.redirect(URL)
    # FIXME: The URL should be HTML-escaped.
    # https://twistedmatrix.com/trac/ticket/9839
    content = b"""
<html>
    <head>
        <meta http-equiv=\"refresh\" content=\"0;URL=%(url)s\">
    </head>
    <body bgcolor=\"#FFFFFF\" text=\"#000000\">
    <a href=\"%(url)s\">click here</a>
    </body>
</html>
""" % {
        b"url": URL
    }
    return content
def redirect(
    request: IRequest, location: URL, origin: Optional[str] = None
) -> KleinRenderable:
    """
    Perform a redirect.
    """
    if origin is not None:
        try:
            location = location.set(origin, request.uri.decode("utf-8"))
        except ValueError:
            return badRequestResponse(request, "Invalid origin URI")

    log.debug(
        "Redirect {source} -> {destination}",
        source=request.uri.decode("utf-8"), destination=location.asText(),
    )
    url = location.asText().encode("utf-8")

    request.setHeader(HeaderName.contentType.value, ContentType.html.value)
    request.setHeader(HeaderName.location.value, url)
    request.setResponseCode(http.FOUND)

    return RedirectPage(location=location)
Beispiel #21
0
def redirect(request: IRequest,
             location: URL,
             origin: Optional[str] = None) -> KleinRenderable:
    """
    Perform a redirect.
    """
    if origin is not None:
        try:
            location = location.set(origin, request.uri.decode("utf-8"))
        except ValueError:
            return badRequestResponse(request, "Invalid origin URI")

    log.debug(
        "Redirect {source} -> {destination}",
        source=request.uri.decode("utf-8"),
        destination=location.asText(),
    )
    url = location.asText().encode("utf-8")

    request.setHeader(HeaderName.contentType.value, ContentType.html.value)
    request.setHeader(HeaderName.location.value, url)
    request.setResponseCode(http.FOUND)

    return RedirectPage(location)
Beispiel #22
0
    async def newIncidentReportResource(
        self, request: IRequest, eventID: str
    ) -> KleinRenderable:
        """
        New incident report endpoint.
        """
        event = Event(id=eventID)
        del eventID

        await self.config.authProvider.authorizeRequest(
            request, event, Authorization.writeIncidentReports
        )

        try:
            json = objectFromJSONBytesIO(request.content)
        except JSONDecodeError as e:
            return invalidJSONResponse(request, e)

        if json.get(IncidentReportJSONKey.event.value, event.id) != event.id:
            return badRequestResponse(
                "Event ID mismatch: "
                f"{json[IncidentReportJSONKey.event.value]} != {event.id}"
            )
        if json.get(IncidentReportJSONKey.incidentNumber.value):
            return badRequestResponse(
                "New incident report may not be attached to an incident: "
                f"{json[IncidentReportJSONKey.incidentNumber.value]}"
            )

        author = request.user.shortNames[0]
        now = DateTime.now(TimeZone.utc)
        jsonNow = jsonObjectFromModelObject(now)

        # Set JSON event id
        # Set JSON incident report number to 0
        # Set JSON incident report created time to now

        for incidentReportKey in (
            IncidentReportJSONKey.number,
            IncidentReportJSONKey.created,
        ):
            if incidentReportKey.value in json:
                return badRequestResponse(
                    request,
                    f"New incident report may not specify "
                    f"{incidentReportKey.value}",
                )

        json[IncidentReportJSONKey.event.value] = event.id
        json[IncidentReportJSONKey.number.value] = 0
        json[IncidentReportJSONKey.created.value] = jsonNow

        # If not provided, set JSON report entries to an empty list

        if IncidentReportJSONKey.reportEntries.value not in json:
            json[IncidentReportJSONKey.reportEntries.value] = []

        # Set JSON report entry created time to now
        # Set JSON report entry author
        # Set JSON report entry automatic=False

        for entryJSON in json[IncidentReportJSONKey.reportEntries.value]:
            for reportEntryKey in (
                ReportEntryJSONKey.created,
                ReportEntryJSONKey.author,
                ReportEntryJSONKey.automatic,
            ):
                if reportEntryKey.value in entryJSON:
                    return badRequestResponse(
                        request,
                        f"New report entry may not specify "
                        f"{reportEntryKey.value}",
                    )

            entryJSON[ReportEntryJSONKey.created.value] = jsonNow
            entryJSON[ReportEntryJSONKey.author.value] = author
            entryJSON[ReportEntryJSONKey.automatic.value] = False

        # Deserialize JSON incident report

        try:
            incidentReport = modelObjectFromJSONObject(json, IncidentReport)
        except JSONCodecError as e:
            return badRequestResponse(request, str(e))

        # Store the incident report

        incidentReport = await self.config.store.createIncidentReport(
            incidentReport, author
        )

        self._log.info(
            "User {author} created new incident report "
            "#{incidentReport.number} via JSON",
            author=author,
            incidentReport=incidentReport,
        )
        self._log.debug(
            "New incident report: {json}",
            json=jsonObjectFromModelObject(incidentReport),
        )

        request.setHeader("Incident-Report-Number", str(incidentReport.number))
        request.setHeader(
            HeaderName.location.value,
            f"{URLs.incidentNumber.asText()}/{incidentReport.number}",
        )
        return noContentResponse(request)
Beispiel #23
0
    async def newIncidentResource(
        self, request: IRequest, eventID: str
    ) -> KleinRenderable:
        """
        New incident endpoint.
        """
        event = Event(id=eventID)
        del eventID

        await self.config.authProvider.authorizeRequest(
            request, event, Authorization.writeIncidents
        )

        try:
            json = objectFromJSONBytesIO(request.content)
        except JSONDecodeError as e:
            return invalidJSONResponse(request, e)

        author = request.user.shortNames[0]
        now = DateTime.now(TimeZone.utc)
        jsonNow = jsonObjectFromModelObject(now)

        # Set JSON incident number to 0
        # Set JSON incident created time to now

        for incidentKey in (
            IncidentJSONKey.number,
            IncidentJSONKey.created,
        ):
            if incidentKey.value in json:
                return badRequestResponse(
                    request, f"New incident may not specify {incidentKey.value}"
                )

        json[IncidentJSONKey.number.value] = 0
        json[IncidentJSONKey.created.value] = jsonNow

        # If not provided, set JSON event, state to new, priority to normal

        if IncidentJSONKey.event.value not in json:
            json[IncidentJSONKey.event.value] = event.id

        if IncidentJSONKey.state.value not in json:
            json[IncidentJSONKey.state.value] = IncidentStateJSONValue.new.value

        if IncidentJSONKey.priority.value not in json:
            json[
                IncidentJSONKey.priority.value
            ] = IncidentPriorityJSONValue.normal.value

        # If not provided, set JSON handles, types, entries,
        # incident report numbers to an empty list

        for incidentKey in (
            IncidentJSONKey.rangerHandles,
            IncidentJSONKey.incidentTypes,
            IncidentJSONKey.reportEntries,
            IncidentJSONKey.incidentReportNumbers,
        ):
            if incidentKey.value not in json:
                json[incidentKey.value] = []

        # Set JSON report entry created time to now
        # Set JSON report entry author
        # Set JSON report entry automatic=False

        for entryJSON in json[IncidentJSONKey.reportEntries.value]:
            for reportEntryKey in (
                ReportEntryJSONKey.created,
                ReportEntryJSONKey.author,
                ReportEntryJSONKey.automatic,
            ):
                if reportEntryKey.value in entryJSON:
                    return badRequestResponse(
                        request,
                        f"New report entry may not specify "
                        f"{reportEntryKey.value}",
                    )

            entryJSON[ReportEntryJSONKey.created.value] = jsonNow
            entryJSON[ReportEntryJSONKey.author.value] = author
            entryJSON[ReportEntryJSONKey.automatic.value] = False

        # Deserialize JSON incident

        try:
            incident = modelObjectFromJSONObject(json, Incident)
        except JSONCodecError as e:
            return badRequestResponse(request, str(e))

        # Validate data

        if incident.event != event:
            return badRequestResponse(
                request,
                f"Incident's event {incident.event} does not match event in "
                f"URL {event}",
            )

        # Store the incident

        incident = await self.config.store.createIncident(incident, author)

        self._log.info(
            "User {author} created new incident #{incident.number} via JSON",
            author=author,
            incident=incident,
        )
        self._log.debug(
            "New incident: {json}", json=jsonObjectFromModelObject(incident)
        )

        request.setHeader("Incident-Number", str(incident.number))
        request.setHeader(
            HeaderName.location.value,
            f"{URLs.incidentNumber.asText()}/{incident.number}",
        )
        return noContentResponse(request)
    async def newIncidentResource(
        self, request: IRequest, eventID: str
    ) -> KleinRenderable:
        """
        New incident endpoint.
        """
        event = Event(id=eventID)

        await self.config.authProvider.authorizeRequest(
            request, event, Authorization.writeIncidents
        )

        try:
            json = objectFromJSONBytesIO(request.content)
        except JSONDecodeError as e:
            return invalidJSONResponse(request, e)

        author = request.user.shortNames[0]
        now = DateTime.now(TimeZone.utc)
        jsonNow = jsonObjectFromModelObject(now)

        # Set JSON incident number to 0
        # Set JSON incident created time to now

        for incidentKey in (
            IncidentJSONKey.number,
            IncidentJSONKey.created,
        ):
            if incidentKey.value in json:
                return badRequestResponse(
                    request,
                    f"New incident may not specify {incidentKey.value}"
                )

        json[IncidentJSONKey.number.value] = 0
        json[IncidentJSONKey.created.value] = jsonNow

        # If not provided, set JSON event, state to new, priority to normal

        if IncidentJSONKey.event.value not in json:
            json[IncidentJSONKey.event.value] = event.id

        if IncidentJSONKey.state.value not in json:
            json[IncidentJSONKey.state.value] = (
                IncidentStateJSONValue.new.value
            )

        if IncidentJSONKey.priority.value not in json:
            json[IncidentJSONKey.priority.value] = (
                IncidentPriorityJSONValue.normal.value
            )

        # If not provided, set JSON handles, types, entries,
        # incident report numbers to an empty list

        for incidentKey in (
            IncidentJSONKey.rangerHandles,
            IncidentJSONKey.incidentTypes,
            IncidentJSONKey.reportEntries,
            IncidentJSONKey.incidentReportNumbers,
        ):
            if incidentKey.value not in json:
                json[incidentKey.value] = []

        # Set JSON report entry created time to now
        # Set JSON report entry author
        # Set JSON report entry automatic=False

        for entryJSON in json[IncidentJSONKey.reportEntries.value]:
            for reportEntryKey in (
                ReportEntryJSONKey.created,
                ReportEntryJSONKey.author,
                ReportEntryJSONKey.automatic,
            ):
                if reportEntryKey.value in entryJSON:
                    return badRequestResponse(
                        request,
                        f"New report entry may not specify "
                        f"{reportEntryKey.value}"
                    )

            entryJSON[ReportEntryJSONKey.created.value] = jsonNow
            entryJSON[ReportEntryJSONKey.author.value] = author
            entryJSON[ReportEntryJSONKey.automatic.value] = False

        # Deserialize JSON incident

        try:
            incident = modelObjectFromJSONObject(json, Incident)
        except JSONCodecError as e:
            return badRequestResponse(request, str(e))

        # Validate data

        if incident.event != event:
            return badRequestResponse(
                request,
                f"Incident's event {incident.event} does not match event in "
                f"URL {event}"
            )

        # Store the incident

        incident = await self.config.store.createIncident(incident, author)

        self._log.info(
            "User {author} created new incident #{incident.number} via JSON",
            author=author, incident=incident
        )
        self._log.debug(
            "New incident: {json}", json=jsonObjectFromModelObject(incident)
        )

        request.setHeader("Incident-Number", incident.number)
        request.setHeader(
            HeaderName.location.value,
            f"{URLs.incidentNumber.asText()}/{incident.number}"
        )
        return noContentResponse(request)
    async def newIncidentReportResource(
        self, request: IRequest
    ) -> KleinRenderable:
        """
        New incident report endpoint.
        """
        await self.config.authProvider.authorizeRequest(
            request, None, Authorization.writeIncidentReports
        )

        try:
            json = objectFromJSONBytesIO(request.content)
        except JSONDecodeError as e:
            return invalidJSONResponse(request, e)

        author = request.user.shortNames[0]
        now = DateTime.now(TimeZone.utc)
        jsonNow = jsonObjectFromModelObject(now)

        # Set JSON incident report number to 0
        # Set JSON incident report created time to now

        for incidentReportKey in (
            IncidentReportJSONKey.number,
            IncidentReportJSONKey.created,
        ):
            if incidentReportKey.value in json:
                return badRequestResponse(
                    request,
                    f"New incident report may not specify "
                    f"{incidentReportKey.value}"
                )

        json[IncidentReportJSONKey.number.value] = 0
        json[IncidentReportJSONKey.created.value] = jsonNow

        # If not provided, set JSON report entries to an empty list

        if IncidentReportJSONKey.reportEntries.value not in json:
            json[IncidentReportJSONKey.reportEntries.value] = []

        # Set JSON report entry created time to now
        # Set JSON report entry author
        # Set JSON report entry automatic=False

        for entryJSON in json[IncidentReportJSONKey.reportEntries.value]:
            for reportEntryKey in (
                ReportEntryJSONKey.created,
                ReportEntryJSONKey.author,
                ReportEntryJSONKey.automatic,
            ):
                if reportEntryKey.value in entryJSON:
                    return badRequestResponse(
                        request,
                        f"New report entry may not specify "
                        f"{reportEntryKey.value}"
                    )

            entryJSON[ReportEntryJSONKey.created.value] = jsonNow
            entryJSON[ReportEntryJSONKey.author.value] = author
            entryJSON[ReportEntryJSONKey.automatic.value] = False

        # Deserialize JSON incident report

        try:
            incidentReport = modelObjectFromJSONObject(json, IncidentReport)
        except JSONCodecError as e:
            return badRequestResponse(request, str(e))

        # Store the incident report

        incidentReport = await self.config.store.createIncidentReport(
            incidentReport, author
        )

        self._log.info(
            "User {author} created new incident report "
            "#{incidentReport.number} via JSON",
            author=author, incidentReport=incidentReport
        )
        self._log.debug(
            "New incident report: {json}",
            json=jsonObjectFromModelObject(incidentReport),
        )

        request.setHeader("Incident-Report-Number", incidentReport.number)
        request.setHeader(
            HeaderName.location.value,
            f"{URLs.incidentNumber.asText()}/{incidentReport.number}"
        )
        return noContentResponse(request)