def test_no_default_exception_view(self) -> None:
        """Test Response Validation Error without default exception view.

        This causes the ResponseValidationError to bubble up to the top, because
        there is no view to render the HTTPException into a response.
        """
        from pyramid.httpexceptions import HTTPPreconditionFailed
        from pyramid_openapi3.exceptions import ResponseValidationError

        # return the exception, so that response validation can kick in
        self._add_view(lambda *arg: HTTPPreconditionFailed())
        # run request through router
        router = Router(self.config.registry)
        environ = {
            "wsgi.url_scheme": "http",
            "SERVER_NAME": "localhost",
            "SERVER_PORT": "8080",
            "REQUEST_METHOD": "GET",
            "PATH_INFO": "/foo",
            "QUERY_STRING": "bar=1",
        }
        start_response = DummyStartResponse()
        with self.assertRaises(ResponseValidationError) as cm:
            router(environ, start_response)
        self.assertEqual(cm.exception.status_code, 500)
        self.assertEqual("Unknown response http status: 412",
                         str(cm.exception))
    def test_response_validation_error(self) -> None:
        """Test View raises ResponseValidationError.

        Example view raises an undefined response code.
        The response validation tween should catch this as response validation error,
        and return an error 500.
        """
        from pyramid.httpexceptions import HTTPPreconditionFailed

        self._add_view(lambda *arg:
                       (_ for _ in ()).throw(HTTPPreconditionFailed()))
        self._add_default_exception_view()
        # run request through router
        router = Router(self.config.registry)
        environ = {
            "wsgi.url_scheme": "http",
            "SERVER_NAME": "localhost",
            "SERVER_PORT": "8080",
            "REQUEST_METHOD": "GET",
            "PATH_INFO": "/foo",
            "HTTP_ACCEPT": "application/json",
            "QUERY_STRING": "bar=1",
        }
        start_response = DummyStartResponse()
        response = router(environ, start_response)
        self.assertEqual(start_response.status, "500 Internal Server Error")
        self.assertIn(
            "Unknown response http status: 412",
            json.loads(b"".join(response))["message"],
        )
Esempio n. 3
0
def check_precondition_headers(viewfunc, request):
    """View decorator to check X-If-[Unm|M]odified-Since headers.

    This decorator checks pre-validated vlaues from the X-If-Modified-Since
    and X-If-Unmodified-Since headers against the actual last-modified
    time of the target resource.  If the preconditions are not met then
    it raises the appropriate error response.

    In addition, any retreived value for the last-modified time will be
    stored in the response headers for return to the client.  This may save
    having to look it up again when the response is being rendered.
    """
    if "if_modified_since" in request.validated:
        ts = get_resource_timestamp(request)
        request.response.headers["X-Last-Modified"] = str(ts)
        if ts <= request.validated["if_modified_since"]:
            raise HTTPNotModified(headers={
                "X-Last-Modified": str(ts),
            })

    if "if_unmodified_since" in request.validated:
        ts = get_resource_timestamp(request)
        request.response.headers["X-Last-Modified"] = str(ts)
        if ts > request.validated["if_unmodified_since"]:
            raise HTTPPreconditionFailed(headers={
                "X-Last-Modified": str(ts),
            })

    return viewfunc(request)
Esempio n. 4
0
def test_process_conditional_put_requests(web_app, pyramid_request, etag,
                                          if_match, if_none_match,
                                          status_code):
    root = pyramid_request.root
    root['resource'] = resource = DummyResource({'foo': 'Hello', 'bar': 123})
    resource.etag = etag
    resource_url = pyramid_request.resource_url(root['resource'])

    headers = {}
    if if_match:
        headers['If-Match'] = if_match
    if if_none_match:
        headers['If-None-Match'] = if_none_match

    kwargs = {'headers': headers}
    if status_code == 304:
        kwargs['exception'] = HTTPNotModified()
    elif status_code == 412:
        kwargs['exception'] = HTTPPreconditionFailed(
            {'etag': None if etag is None else etag.serialize()})
    else:
        kwargs['status'] = status_code

    new_params = {'foo': 'World', 'bar': 456}
    web_app.put_json(resource_url, params=new_params, **kwargs)
Esempio n. 5
0
def _assert_put_and_patch(usage_examples: UsageExamples, web_app: WebApp):
    """Test PUT and PATH requests."""
    info_name = usage_examples.__class__.__name__
    test_params = [
        ('PUT', usage_examples.put_requests),
        ('PATCH', usage_examples.patch_requests),
    ]
    for http_method, examples_method in test_params:
        send = PutPatchRequestsTester(web_app, usage_examples,
                                      http_method.lower())
        if examples_method:
            with usage_examples.send_function(send):
                examples_method()
        if http_method not in usage_examples.allowed_methods:
            assert send.calls_count == 0, '{} sends {} requests to resource'.format(
                info_name, http_method)
            continue

        assert send.calls_count > 0, '{} has not any {} requests'.format(
            info_name, http_method)

        etag = usage_examples.resource.get_etag()
        if etag:
            # if 'HEAD' in resource_examples.allowed_methods:
            #     params, headers = resource_examples.authorize_request(None, None, None)
            #     head_res = web_app.head(resource_examples.resource_url, params=params, headers=headers)
            #     etag = head_res.headers['ETag']
            resource = usage_examples.resource
            parent = resource.__parent__
            if parent and 'GET' in usage_examples.allowed_methods:
                # Get a new resource instance with refreshed internal state
                etag = parent[resource.__name__].get_etag().serialize()
            else:
                # WARNING: This value of etag may be obsolete
                etag = etag.serialize()

            if not send.was_if_match:
                send(
                    headers={'If-Match': '"__bad_etag__"'},
                    exception=HTTPPreconditionFailed({'etag': etag}),
                )
            if not send.was_if_none_match:
                send(
                    headers={'If-None-Match': etag},
                    exception=HTTPPreconditionFailed({'etag': etag}),
                )
Esempio n. 6
0
def blob_transform_view(request):
    blobstore = request.repository.blob
    if not blobstore.blob_exists(request.context.model.id):
        raise HTTPPreconditionFailed('File is missing')

    blobstore.transform_blob(request.context)
    blobstore.finalize_blob(request.context)
    return request.context.model.to_dict()
Esempio n. 7
0
def blob_upload_local_view(request):
    blobstore = request.repository.blob
    if blobstore.blob_exists(request.context.model.id):
        raise HTTPPreconditionFailed()

    blobstore.receive_blob(request, request.context)
    request.context.put()
    return BlobSchema().to_json(request.context.model.to_dict())
Esempio n. 8
0
def blob_preview_local_view(request):
    # this is specific for the local implementation,
    preview_kind = request.context.model.info.get('preview_blob')
    if not preview_kind:
        raise HTTPPreconditionFailed('Preview is missing')
    request.repository.blob.backend.serve_preview_blob(request,
                                                       request.response,
                                                       request.context)
    return request.response
Esempio n. 9
0
def blob_download_local_view(request):
    blobstore = request.repository.blob
    if not blobstore.blob_exists(request.context.model.id):
        raise HTTPPreconditionFailed('File is missing')
    response = request.response
    response.content_type = request.context.model.format
    response.content_length = request.context.model.bytes
    request.repository.blob.serve_blob(request, response, request.context)
    return response
Esempio n. 10
0
 def wrapped(context, request):
     if_match = str(request.if_match)
     if if_match == '*':
         return view_callable(context, request)
     uuid_tid = (v.split('=', 1) for v in if_match.strip('"').split('&'))
     root = request.root
     mismatching = (root.get_by_uuid(uuid).tid != UUID(tid)
                    for uuid, tid in uuid_tid)
     if any(mismatching):
         raise HTTPPreconditionFailed("The resource has changed.")
     return view_callable(context, request)
Esempio n. 11
0
def blob_transform_view(request):
    blob_key = request.matchdict['blob_key']
    request.context.from_blob_key(blob_key)
    if request.context.model is None:
        raise HTTPNotFound()
    blobstore = request.repository.blob
    if not blobstore.blob_exists(blob_key):
        raise HTTPPreconditionFailed('File is missing')

    blobstore.transform_blob(request.context)
    return request.context.model.to_dict()
Esempio n. 12
0
    def _raise_412_if_modified(self, record=None):
        """Raise 412 if current timestamp is superior to the one
        specified in headers.

        :raises:
            :exc:`~pyramid:pyramid.httpexceptions.HTTPPreconditionFailed`
        """
        if_match = self.request.headers.get('If-Match')
        if_none_match = self.request.headers.get('If-None-Match')

        if not if_match and not if_none_match:
            return

        error_details = {
            'location': 'header',
            'description': ("Invalid value for If-Match. The value should "
                            "be integer between double quotes.")}

        try:
            if_match = decode_header(if_match) if if_match else None
            if_none_match = decode_header(if_none_match) if if_none_match else None
        except UnicodeDecodeError:
            raise_invalid(self.request, **error_details)

        if record and if_none_match == '*':
            if record.get(self.model.deleted_field, False):
                # Tombstones should not prevent creation.
                return
            modified_since = -1  # Always raise.
        elif if_match:
            try:
                if not (if_match[0] == if_match[-1] == '"'):
                    raise ValueError()
                modified_since = int(if_match[1:-1])
            except (IndexError, ValueError):
                raise_invalid(self.request, **error_details)
        else:
            # In case _raise_304_if_not_modified() did not raise.
            return

        if record:
            current_timestamp = record[self.model.modified_field]
        else:
            current_timestamp = self.model.timestamp()

        if current_timestamp > modified_since:
            error_msg = 'Resource was modified meanwhile'
            details = {'existing': record} if record else {}
            response = http_error(HTTPPreconditionFailed(),
                                  errno=ERRORS.MODIFIED_MEANWHILE,
                                  message=error_msg,
                                  details=details)
            self._add_timestamp_header(response, timestamp=current_timestamp)
            raise response
Esempio n. 13
0
    def _raise_412_if_modified(self, obj=None):
        """Raise 412 if current timestamp is superior to the one
        specified in headers.

        :raises:
            :exc:`~pyramid:pyramid.httpexceptions.HTTPPreconditionFailed`
        """
        if_match = self.request.validated["header"].get("If-Match")
        if_none_match = self.request.validated["header"].get("If-None-Match")

        # Check if object exists
        object_exists = obj is not None

        # If no precondition headers, just ignore
        if not if_match and not if_none_match:
            return

        # If-None-Match: * should always raise if an object exists
        if if_none_match == "*" and object_exists:
            modified_since = -1  # Always raise.

        # If-Match should always raise if an object doesn't exist
        elif if_match and not object_exists:
            modified_since = -1

        # If-Match with ETag value on existing objects should compare ETag
        elif if_match and if_match != "*":
            modified_since = if_match

        # If none of the above applies, don't raise
        else:
            return

        if obj:
            current_timestamp = obj[self.model.modified_field]
        else:
            current_timestamp = self.model.timestamp()

        if current_timestamp != modified_since:
            error_msg = "Resource was modified meanwhile"
            # Do not provide the permissions among the object fields.
            # Ref: https://github.com/Kinto/kinto/issues/224
            existing = {**obj} if obj else {}
            existing.pop(self.model.permissions_field, None)

            details = {"existing": existing} if obj else {}
            response = http_error(
                HTTPPreconditionFailed(),
                errno=ERRORS.MODIFIED_MEANWHILE,
                message=error_msg,
                details=details,
            )
            self._add_timestamp_header(response, timestamp=current_timestamp)
            raise response
Esempio n. 14
0
def blob_upload_local_view(request):
    blob_key = request.matchdict['blob_key']

    request.context.from_blob_key(request.matchdict['blob_key'])
    if request.context.model is None:
        raise HTTPNotFound()

    blobstore = request.repository.blob
    if blobstore.blob_exists(blob_key):
        raise HTTPPreconditionFailed()

    blobstore.receive_blob(request, request.context)
    return BlobSchema().to_json(request.context.model.to_dict())
Esempio n. 15
0
    def mapped_view(context, request: PyramidRequest):
        if context is not request.root:
            if_match = request.if_match
            if_none_match = request.if_none_match
            if if_match is not AnyETag or if_none_match is not NoETag:
                etag = context.get_etag()
                if etag is None:
                    if None not in if_match:
                        raise HTTPPreconditionFailed({'etag': None})
                else:
                    # https://tools.ietf.org/html/rfc7232#section-6
                    # https://tools.ietf.org/html/rfc7232#section-2.3.2
                    if if_match is not AnyETag:
                        if not etag.is_strict or etag.value not in if_match:
                            raise HTTPPreconditionFailed(
                                {'etag': etag.serialize()})
                    if etag.value in if_none_match:
                        if request.method in ('GET', 'HEAD'):
                            raise HTTPNotModified()
                        raise HTTPPreconditionFailed(
                            {'etag': etag.serialize()})

        return view(context, request)
Esempio n. 16
0
    def _raise_412_if_modified(self, record=None):
        """Raise 412 if current timestamp is superior to the one
        specified in headers.

        :raises:
            :exc:`~pyramid:pyramid.httpexceptions.HTTPPreconditionFailed`
        """
        if_match = self.request.validated["header"].get("If-Match")
        if_none_match = self.request.validated["header"].get("If-None-Match")

        # Check if record exists
        record_exists = record is not None

        # If no precondition headers, just ignore
        if not if_match and not if_none_match:
            return

        # If-None-Match: * should always raise if a record exists
        if if_none_match == "*" and record_exists:
            modified_since = -1  # Always raise.

        # If-Match should always raise if a record doesn't exist
        elif if_match and not record_exists:
            modified_since = -1

        # If-Match with ETag value on existing records should compare ETag
        elif if_match and if_match != "*":
            modified_since = if_match

        # If none of the above applies, don't raise
        else:
            return

        if record:
            current_timestamp = record[self.model.modified_field]
        else:
            current_timestamp = self.model.timestamp()

        if current_timestamp != modified_since:
            error_msg = "Resource was modified meanwhile"
            details = {"existing": record} if record else {}
            response = http_error(
                HTTPPreconditionFailed(),
                errno=ERRORS.MODIFIED_MEANWHILE,
                message=error_msg,
                details=details,
            )
            self._add_timestamp_header(response, timestamp=current_timestamp)
            raise response
    def test_response_validation_error(self) -> None:
        """Test View raises ResponseValidationError.

        Example view raises an undefined response code.
        The response validation tween should catch this as response validation error,
        and return an error 500.
        """
        from pyramid.httpexceptions import HTTPPreconditionFailed

        self._add_view(lambda *arg: HTTPPreconditionFailed())

        # run request through router
        router = Router(self.config.registry)
        environ = {
            "wsgi.url_scheme": "http",
            "SERVER_NAME": "localhost",
            "SERVER_PORT": "8080",
            "REQUEST_METHOD": "GET",
            "PATH_INFO": "/foo",
            "HTTP_ACCEPT": "application/json",
            "QUERY_STRING": "bar=1",
        }
        start_response = DummyStartResponse()
        with self.assertLogs(level="ERROR") as cm:
            response = router(environ, start_response)
        self.assertEqual(start_response.status, "500 Internal Server Error")
        if openapi_core.__version__ == "0.13.8":  # pragma: no cover
            self.assertEqual(
                json.loads(response[0]),
                [
                    {
                        "exception": "ResponseNotFound",
                        "message": "Unknown response http status: 412",
                    }
                ],
            )
        else:  # pragma: no cover
            self.assertEqual(
                json.loads(response[0]),
                [
                    {
                        "exception": "InvalidResponse",
                        "message": "Unknown response http status: 412",
                    }
                ],
            )
        self.assertEqual(
            cm.output, ["ERROR:pyramid_openapi3:Unknown response http status: 412"]
        )
def delete_key(request):
    """Delete a key.

    You must have a valid session and be authenticated as the target user.
    """
    appid = request.matchdict["appid"].encode("utf8")
    userid = request.matchdict["userid"].encode("utf8")
    key = request.matchdict["key"].encode("utf8")
    if_match = _get_if_match(request)
    store = request.registry.getUtility(ISauropodBackend)
    try:
        store.delete(appid, userid, key, if_match=if_match)
    except KeyError:
        raise HTTPNotFound()
    except ConflictError:
        raise HTTPPreconditionFailed()
    return HTTPNoContent()
Esempio n. 19
0
    def security_tween(request):
        login = None
        expected_user = request.headers.get('X-If-Match-User')
        if expected_user is not None:
            login = request.authenticated_userid
            if login != 'mailto.' + expected_user:
                detail = 'X-If-Match-User does not match'
                raise HTTPPreconditionFailed(detail)

        # wget may only send credentials following a challenge response.
        auth_challenge = asbool(request.headers.get('X-Auth-Challenge', False))
        if auth_challenge or request.authorization is not None:
            login = request.authenticated_userid
            if login is None:
                raise HTTPUnauthorized(headerlist=forget(request))

        if request.method in ('GET', 'HEAD'):
            return handler(request)

        if request.content_type != 'application/json':
            detail = "%s is not 'application/json'" % request.content_type
            raise HTTPUnsupportedMediaType(detail)

        token = request.headers.get('X-CSRF-Token')
        if token is not None:
            # Avoid dirtying the session and adding a Set-Cookie header
            # XXX Should consider if this is a good idea or not and timeouts
            if token == dict.get(request.session, '_csrft_', None):
                return handler(request)
            raise CSRFTokenError('Incorrect CSRF token')

        # NOTE: cutting out CSRF protection here ... why protect against CSRF if you provide an
        # unathenticated endpoint that will delivery the CSRF token? I'm looking at you /session.
        # this should be revisted, either embed the csrf token in the index.html as part of the
        # rendering subprocess somehow, or return it from the login view and let the client store it
        # but of course that sounds a lot like JWT...
        return handler(request)

        if login is None:
            login = request.authenticated_userid
        if login is not None:
            namespace, userid = login.split('.', 1)
            if namespace not in ('mailto', 'persona'):
                return handler(request)
        raise CSRFTokenError('Missing CSRF token')
def set_key(request):
    """Update the value of a key.

    You must have a valid session and be authenticated as the target user.
    """
    appid = request.matchdict["appid"].encode("utf8")
    userid = request.matchdict["userid"].encode("utf8")
    key = request.matchdict["key"].encode("utf8")
    store = request.registry.getUtility(ISauropodBackend)
    value = request.POST.get("value")
    if value is None:
        raise HTTPBadRequest("mising value")
    if_match = _get_if_match(request)
    try:
        item = store.set(appid, userid, key, value, if_match=if_match)
    except ConflictError:
        raise HTTPPreconditionFailed()
    r = HTTPNoContent()
    if item.etag:
        r.headers["ETag"] = item.etag
    return r
Esempio n. 21
0
def _assert_get_and_head(usage_examples: UsageExamples, web_app: WebApp):
    """Test GET requests."""
    info_name = usage_examples.__class__.__name__
    send = GetRequestsTester(web_app, usage_examples)
    if usage_examples.get_requests:
        with usage_examples.send_function(send):
            usage_examples.get_requests()
    if 'GET' not in usage_examples.allowed_methods:
        assert send.calls_count == 0, '{} sends GET requests to resource'.format(
            info_name)
        return

    assert send.calls_count > 0, '{} has not any GET requests'.format(
        info_name)

    etag = usage_examples.resource.get_etag()
    if etag:
        etag = etag.serialize()
        if not send.was_if_match:
            send(
                headers={'If-Match': '"__bad_etag__"'},
                exception=HTTPPreconditionFailed({'etag': etag}),
            )
        if not send.was_if_none_match:
            send(
                headers={'If-None-Match': etag},
                exception=HTTPNotModified,
            )

    # Test listing of embedded resources
    if (IHalResourceWithEmbeddedView.providedBy(usage_examples.view)
            and usage_examples.test_listing):
        orig_listing_conf = deepcopy(LISTING_CONF)
        try:
            assert_container_listing(usage_examples, web_app)
        finally:
            LISTING_CONF.clear()
            LISTING_CONF.update(orig_listing_conf)
Esempio n. 22
0
    def security_tween(request):
        login = None
        expected_user = request.headers.get('X-If-Match-User')
        if expected_user is not None:
            login = request.authenticated_userid
            if login != 'mailto.' + expected_user:
                detail = 'X-If-Match-User does not match'
                raise HTTPPreconditionFailed(detail)

        # wget may only send credentials following a challenge response.
        auth_challenge = asbool(request.headers.get('X-Auth-Challenge', False))
        if auth_challenge or request.authorization is not None:
            login = request.authenticated_userid
            if login is None:
                raise HTTPUnauthorized(headerlist=forget(request))

        if request.method in ('GET', 'HEAD'):
            return handler(request)

        if request.content_type != 'application/json':
            detail = "%s is not 'application/json'" % request.content_type
            raise HTTPUnsupportedMediaType(detail)

        token = request.headers.get('X-CSRF-Token')
        if token is not None:
            # Avoid dirtying the session and adding a Set-Cookie header
            # XXX Should consider if this is a good idea or not and timeouts
            if token == dict.get(request.session, '_csrft_', None):
                return handler(request)
            raise CSRFTokenError('Incorrect CSRF token')

        if login is None:
            login = request.authenticated_userid
        if login is not None:
            namespace, userid = login.split('.', 1)
            if namespace not in ('mailto', 'persona'):
                return handler(request)
        raise CSRFTokenError('Missing CSRF token')
Esempio n. 23
0
    def _raise_412_if_modified(self, record=None):
        """Raise 412 if current timestamp is superior to the one
        specified in headers.

        :raises:
            :exc:`~pyramid:pyramid.httpexceptions.HTTPPreconditionFailed`
        """
        if_match = self.request.validated['header'].get('If-Match')
        if_none_match = self.request.validated['header'].get('If-None-Match')

        if not if_match and not if_none_match:
            return

        if record and if_none_match == '*':
            if record.get(self.model.deleted_field, False):
                # Tombstones should not prevent creation.
                return
            modified_since = -1  # Always raise.
        elif if_match:
            modified_since = if_match
        else:
            # In case _raise_304_if_not_modified() did not raise.
            return

        if record:
            current_timestamp = record[self.model.modified_field]
        else:
            current_timestamp = self.model.timestamp()

        if current_timestamp > modified_since:
            error_msg = 'Resource was modified meanwhile'
            details = {'existing': record} if record else {}
            response = http_error(HTTPPreconditionFailed(),
                                  errno=ERRORS.MODIFIED_MEANWHILE,
                                  message=error_msg,
                                  details=details)
            self._add_timestamp_header(response, timestamp=current_timestamp)
            raise response
Esempio n. 24
0
    def _get_examples_info(self, usage_examples: UsageExamples,
                           method: str) -> List[structs.ExampleInfo]:
        """Execute all examples of method and return list of ExampleInfo."""
        send_requests = getattr(usage_examples, '%s_requests' % method, None)
        if send_requests is None:
            return []

        send = _ExampleInfoCollector(self.web_app, usage_examples, method)
        with usage_examples.send_function(send):
            send_requests()

        if send.results and method in ('head', 'get'):
            etag = usage_examples.resource.get_etag()
            if etag:
                etag = etag.serialize()
                if all(not res.request_info.headers
                       or 'If-Match' not in res.request_info.headers
                       for res in send.results):
                    send(
                        headers={'If-Match': '"__bad_etag__"'},
                        exception=HTTPPreconditionFailed({'etag': etag}),
                    )
                if all(not res.request_info.headers
                       or 'If-None-Match' not in res.request_info.headers
                       for res in send.results):
                    send(
                        headers={'If-None-Match': etag},
                        exception=HTTPNotModified,
                    )
        elif send.results and method in ('put', 'patch'):
            etag = usage_examples.resource.get_etag()
            if etag:
                etag = etag.serialize()
                if all(not res.request_info.headers
                       or 'If-Match' not in res.request_info.headers
                       for res in send.results):
                    send(
                        headers={'If-Match': '"__bad_etag__"'},
                        exception=HTTPPreconditionFailed({'etag': ANY}),
                    )
                if all(not res.request_info.headers
                       or 'If-None-Match' not in res.request_info.headers
                       for res in send.results):
                    # if 'HEAD' in usage_examples.allowed_methods:
                    #     params, headers = usage_examples.authorize_request(None, None, None)
                    #     head_res = self.web_app.head(usage_examples.resource_url, params=params, headers=headers)
                    #     etag = head_res.headers['ETag']
                    resource = usage_examples.resource
                    parent = resource.__parent__
                    if parent and 'GET' in usage_examples.allowed_methods:
                        # Get a new resource instance with refreshed internal state
                        etag = parent[resource.__name__].get_etag().serialize()
                    else:
                        # WARNING: This value of etag may be obsolete
                        etag = etag.serialize()
                    send(
                        headers={'If-None-Match': etag},
                        exception=HTTPPreconditionFailed({'etag': ANY}),
                    )

        return send.results
def server_id_response(request):
    request.response = HTTPPreconditionFailed()
    request.response.empty_body = True
    request.add_response_callback(server_id_callback)
    return request.response
Esempio n. 26
0
def precondition_failed_redirect(request):
    '''
    Errors get redirected here
    '''
    return HTTPPreconditionFailed()
Esempio n. 27
0
    def auditstream_sse(self):
        """Returns an event stream suitable for driving an HTML5 EventSource.
           The event stream will contain auditing events.

           Obtain events for the context of the view only::

            var source = new EventSource(
               "${request.sdiapi.mgmt_path(context, 'auditstream-sse')}");
           
           Obtain events for a single OID unrelated to the context::

            var source = new EventSource(
               "${request.sdiapi.mgmt_path(context, 'auditstream-sse', query={'oid':'12345'})}");

           Obtain events for a set of OIDs::

            var source = new EventSource(
               "${request.sdiapi.mgmt_path(context, 'auditstream-sse', query={'oid':['12345', '56789']})}");

           Obtain all events for all oids::

            var source = new EventSource(
               "${request.sdiapi.mgmt_path(context, 'auditstream-sse', query={'all':'1'})}");
           
           The executing user will need to possess the ``sdi.view-auditstream``
           permission against the context on which the view is invoked.
        """
        request = self.request
        response = request.response
        response.content_type = 'text/event-stream'
        last_event_id = request.headers.get('Last-Event-Id')
        log = self.get_auditlog(self.context)
        if log is None:
            return HTTPPreconditionFailed('Auditing not configured')
        if not last_event_id:
            # first call, set a baseline event id
            gen, idx = log.latest_id()
            msg = compose_message('%s-%s' % (gen, idx))
            response.text = msg
            self.logger.debug('New SSE connection on %s, returning %s' %
                              (request.url, msg))
            return response
        else:
            if request.GET.get('all'):
                oids = ()
            elif request.GET.get('oid'):
                oids = map(int, request.GET.getall('oid'))
            else:
                oids = [get_oid(self.context)]
            _gen, _idx = map(int, last_event_id.split('-', 1))
            events = log.newer(_gen, _idx, oids=oids)
            msg = text_type('')
            for gen, idx, event in events:
                event_id = '%s-%s' % (gen, idx)
                message = compose_message(event_id, event.name, event.payload)
                msg += message
            self.logger.debug(
                'SSE connection on %s with id %s-%s, returning %s' %
                (request.url, _gen, _idx, msg))
            response.text = msg
            return response